mirror of
https://github.com/LCTT/TranslateProject.git
synced 2025-01-10 22:21:11 +08:00
commit
39a033f4a6
@ -1,9 +1,9 @@
|
||||
Linux 命令行工具使用小贴士及技巧 ——(一)
|
||||
Linux 命令行工具使用小贴士及技巧(一)
|
||||
============================================================
|
||||
|
||||
### 相关内容
|
||||
|
||||
如果你刚开始在 Linux 系统中使用命令行工具,那么应该了解它是 Linux 操作系统中功能最强大和有用的工具之一。学习的难易程度跟你想研究的深度有关。但是,无论你的技术能力水平怎么样,这篇文章中的一些小贴士和技巧都会对你有所帮助。
|
||||
如果你刚开始在 Linux 系统中使用命令行工具,那么你应该知道它是 Linux 操作系统中功能最强大和有用的工具之一。学习的难易程度跟你想研究的深度有关。但是,无论你的技术能力水平怎么样,这篇文章中的一些小贴士和技巧都会对你有所帮助。
|
||||
|
||||
在本系列的文章中,我们将会讨论一些非常有用的命令行工具使用小技巧,希望你的命令行使用体验更加愉快。
|
||||
|
||||
@ -15,11 +15,11 @@ Linux 命令行工具使用小贴士及技巧 ——(一)
|
||||
|
||||
#### 轻松切换目录 —— 快捷方式
|
||||
|
||||
假设你正在命令行下做一些操作,并且你需要经常在两个目录间来回切换。而且这两个目录在完全不同的两个路径下,比如说,分别在 /home/ 和 /usr/ 下。你会怎么做呢?
|
||||
假设你正在命令行下做一些操作,并且你需要经常在两个目录间来回切换。而且这两个目录在完全不同的两个路径下,比如说,分别在 `/home/` 和 `/usr/` 下。你会怎么做呢?
|
||||
|
||||
其中,最简单直接的方式就是输入这些目录的全路径。虽然这种方式本身没什么问题,但是却很浪费时间。另外一种方式就是打开两个终端窗口分别进行操作。但是这两种方式使用起来既不方便,也显得没啥技术含量。
|
||||
|
||||
你应该感到庆幸的是,还有另外一种更为简捷的方法来解决这个问题。你需要做的就是先手动切换到这两个目录(通过 **cd** 命令分别加上各自的路径),之后你就可以使用 **cd -** 命令在两个目录之间来回快速切换了。
|
||||
你应该感到庆幸的是,还有另外一种更为简捷的方法来解决这个问题。你需要做的就是先手动切换到这两个目录(通过 `cd` 命令分别加上各自的路径),之后你就可以使用 `cd -` 命令在两个目录之间来回快速切换了。
|
||||
|
||||
例如:
|
||||
|
||||
@ -30,35 +30,35 @@ $ pwd
|
||||
/home/himanshu/Downloads
|
||||
```
|
||||
|
||||
然后,我切换到 /usr/ 路径下的其它目录:
|
||||
然后,我切换到 `/usr/` 路径下的其它目录:
|
||||
|
||||
```
|
||||
cd /usr/lib/
|
||||
```
|
||||
|
||||
现在,我可以很方便的使用下面的命令来向前向后快速地切换到两个目录:
|
||||
现在,我可以很方便的使用下面的命令来向前、向后快速地切换到两个目录:
|
||||
|
||||
```
|
||||
cd -
|
||||
```
|
||||
|
||||
下面是 **cd -** 命令的操作截图:
|
||||
下面是 `cd -` 命令的操作截图:
|
||||
|
||||
[
|
||||
![The Linux cd command](https://www.howtoforge.com/images/linux-command-line-tips-for-beginners/cmd-line-tips.png)
|
||||
][5]
|
||||
|
||||
有一点我得跟大家强调下,如果你在操作的过程中使用 cd 加路径的方式切换到第三个目录下,那么 **cd -** 命令将应用于当前目录及第三个目录之间进行切换。
|
||||
有一点我得跟大家强调下,如果你在操作的过程中使用 `cd` 加路径的方式切换到第三个目录下,那么 `cd -` 命令将应用于当前目录及第三个目录之间进行切换。
|
||||
|
||||
#### 轻松切换目录 —— 相关细节
|
||||
|
||||
对于那些有强烈好奇心的用户,他们想搞懂 **cd -** 的工作原理,解释如下:如大家所知道的那样, cd 命令需要加上一个路径作为它的参数。现在,当 - 符号作为参数传输给 cd 命令时,它将被 OLDPWD 环境变量的值所替代。
|
||||
对于那些有强烈好奇心的用户,他们想搞懂 `cd -` 的工作原理,解释如下:如大家所知道的那样, `cd` 命令需要加上一个路径作为它的参数。现在,当 `-` 符号作为参数传输给 `cd` 命令时,它将被 `OLDPWD` 环境变量的值所替代。
|
||||
|
||||
[
|
||||
![The cd command explained](https://www.howtoforge.com/images/linux-command-line-tips-for-beginners/cmd-line-tips-oldpwd.png)
|
||||
][6]
|
||||
|
||||
现在应该明白了吧, OLDPWD 环境变量存储的是前一个操作目录的路径。这个解释在 cd 命令的 man 帮助文档中有说明,但是,很遗憾的是你的系统中可能没有预先安装 man 命令帮助工具(至少在 Ubuntu 系统下没有安装)。
|
||||
现在应该明白了吧, `OLDPWD` 环境变量存储的是前一个操作目录的路径。这个解释在 `cd` 命令的 man 帮助文档中有说明,但是,很遗憾的是你的系统中可能没有预先安装 `man` 命令帮助工具(至少在 Ubuntu 系统下没有安装)。
|
||||
|
||||
但是,安装这个 man 帮助工具也很简单,你只需要执行下的安装命令即可:
|
||||
|
||||
@ -80,43 +80,43 @@ man cd
|
||||
cd "$OLDPWD" && pwd
|
||||
```
|
||||
|
||||
毫无疑问, cd 命令设置了 OLDPWD 环境变量值。因此每一次你切换操作目录时,上一个目录的路径就会被保存到这个变量里。这还让我们看到很重要的一点就是:任何时候启动一个新的 shell 实例(包括手动执行或是使用 shell 脚本),都不存在 ‘上一个工作目录’。
|
||||
毫无疑问, `cd` 命令设置了 `OLDPWD` 环境变量值。因此每一次你切换操作目录时,上一个目录的路径就会被保存到这个变量里。这还让我们看到很重要的一点就是:任何时候启动一个新的 shell 实例(包括手动执行或是使用 shell 脚本),都不存在 ‘上一个工作目录’。
|
||||
|
||||
[
|
||||
![Hyphen and the cd command](https://www.howtoforge.com/images/linux-command-line-tips-for-beginners/cmd-line-tips-no-oldpwd.png)
|
||||
][7]
|
||||
|
||||
这也很符合逻辑,因为 cd 命令设置了 OLDPWD 环境变量值。因此,除非你至少执行了一次 cd 命令,否则 OLDPWD 环境变量不会包含任何值。
|
||||
这也很符合逻辑,因为 `cd` 命令设置了 `OLDPWD` 环境变量值。因此,除非你至少执行了一次 `cd` 命令,否则 `OLDPWD` 环境变量不会包含任何值。
|
||||
|
||||
继续,尽管这有些难以理解, **cd -** 和 **cd $OLDWPD** 命令的执行结果并非在所有环境下都相同。比如说,你重新打开一个新的 shell 窗口时。
|
||||
继续,尽管这有些难以理解, `cd -` 和 `cd $OLDWPD` 命令的执行结果并非在所有环境下都相同。比如说,你重新打开一个新的 shell 窗口时。
|
||||
|
||||
[
|
||||
![cd command example](https://www.howtoforge.com/images/linux-command-line-tips-for-beginners/cmd-line-tips-oldpwd-home.png)
|
||||
][8]
|
||||
|
||||
从上面的截图可以清楚的看出,当执行 **cd -** 命令提示未设置 OLDPWD 值时, **cd $OLDPWD** 命令没有报任何错;实际上,它把当前的工作目录改变到用户的 home 目录里。
|
||||
从上面的截图可以清楚的看出,当执行 `cd -` 命令提示未设置 `OLDPWD` 值时, `cd $OLDPWD` 命令没有报任何错;实际上,它把当前的工作目录改变到用户的 home 目录里。
|
||||
|
||||
那是因为 OLDPWD 变量目前还没有被设置, $OLDPWD 仅仅是一个空字符串。因此, **cd $OLDPWD** 命令跟 **cd** 命令的执行结果是一致的,默认情况下,会把用户当前的工作目录切换到用户的 home 目录里。
|
||||
那是因为 `OLDPWD` 变量目前还没有被设置, `$OLDPWD` 仅仅是一个空字符串。因此, `cd $OLDPWD` 命令跟 `cd` 命令的执行结果是一致的,默认情况下,会把用户当前的工作目录切换到用户的 home 目录里。
|
||||
|
||||
最后,我还遇到过这样的要求,需要让 **cd -** 命令执行的结果不显示出来。我的意思是,有这样的情况(比如说,在写 shell 脚本的时候),你想让 **cd -** 命令的执行结果不要把目录信息显示出来。那种情况下,你就可以使用下面的命令方式了:
|
||||
最后,我还遇到过这样的要求,需要让 `cd -` 命令执行的结果不显示出来。我的意思是,有这样的情况(比如说,在写 shell 脚本的时候),你想让 `cd -` 命令的执行结果不要把目录信息显示出来。那种情况下,你就可以使用下面的命令方式了:
|
||||
|
||||
```
|
||||
cd - &>/dev/null
|
||||
```
|
||||
|
||||
上面的命令把文件描述符 2(标准输入)和 1(标准输出)的结果重定向到 [/dev/null][9] 目录。这意味着,这个命令产生的所有的错误不会显示出来。但是,你也可以使用通用的 [$? 方式][10]来检查这个命令的执行是否异常。如果这个命令执行报错, **echo $?** 将会返回 ‘1’,否则返回 ‘0’。
|
||||
上面的命令把文件描述符 2(标准输入)和 1(标准输出)的结果重定向到 [`/dev/null`][9] 目录。这意味着,这个命令产生的所有的错误不会显示出来。但是,你也可以使用通用的 [`$?` 方式][10]来检查这个命令的执行是否异常。如果这个命令执行报错, `echo $?` 将会返回 `1`,否则返回 `0`。
|
||||
|
||||
或者说,如果你觉得 **cd -** 命令出错时输出信息没有关系,你也可以使用下面的命令来代替:
|
||||
或者说,如果你觉得 `cd -` 命令出错时输出信息没有关系,你也可以使用下面的命令来代替:
|
||||
|
||||
```
|
||||
cd - > /dev/null
|
||||
```
|
||||
|
||||
这个命令仅用于将文件描述符 1 (标准输出)重定向到 '/dev/null' 。
|
||||
这个命令仅用于将文件描述符 1 (标准输出)重定向到 `/dev/null` 。
|
||||
|
||||
### 总结
|
||||
|
||||
遗憾的是,这篇文章仅包含了一个跟命令行相关的小技巧,但是,我们已经地对 **cd -** 命令的使用进行了深入地探讨。建议你在自己的 Linux 系统的命令行终端中测试本文中的实例。此外,也强烈建议你查看 man 帮助文档,然后对 cd 命令进行全面测试。
|
||||
遗憾的是,这篇文章仅包含了一个跟命令行相关的小技巧,但是,我们已经地对 `cd -` 命令的使用进行了深入地探讨。建议你在自己的 Linux 系统的命令行终端中测试本文中的实例。此外,也强烈建议你查看 man 帮助文档,然后对 cd 命令进行全面测试。
|
||||
|
||||
如果你对这篇文章有什么疑问,请在下面的评论区跟大家交流。同时,敬请关注下一篇文章,我们将以同样的方式探讨更多有用的命令行使用技巧。
|
||||
|
@ -1,52 +1,51 @@
|
||||
|
||||
在独立的 Root 和 Home 硬盘驱动器上安装 Ubuntu
|
||||
============================================================
|
||||
|
||||
![](https://maketecheasier-2d0f.kxcdn.com/assets/uploads/2017/02/ubuntu-feature-image.jpg "How to Install Ubuntu with Separate Root and Home Hard Drivess")
|
||||
|
||||
安装 Linux 系统时,可以有两种不同的方式。第一种方式是在一个超快的固态硬盘上进行安装,这样可以保证迅速开机和高速访问数据。第二种方式是在一个较慢但很强大的普通硬盘驱动上安装,这样的硬盘转速很快并且存储容量很大,从而可以存储大量的应用程序和数据。
|
||||
安装 Linux 系统时,可以有两种不同的方式。第一种方式是在一个超快的固态硬盘上进行安装,这样可以保证迅速开机和高速访问数据。第二种方式是在一个较慢但很强大的普通硬盘驱动器上安装,这样的硬盘转速快并且存储容量大,从而可以存储大量的应用程序和数据。
|
||||
|
||||
然而,一些 Linux 用户都知道,[固态硬盘][10]很棒,但是又很贵,而普通硬盘容量很大但速度较慢。如果我告诉你,可以同时利用两种硬盘来安装 Linux 系统,会怎么样?一个超快、现代化的固态硬盘驱动 Linux 内核,一个容量很大的普通硬盘来存储其他数据。
|
||||
然而,一些 Linux 用户都知道,固态硬盘很棒,但是又很贵,而普通硬盘容量很大但速度较慢。如果我告诉你,可以同时利用两种硬盘来安装 Linux 系统,会怎么样?一个超快、现代化的固态硬盘驱动 Linux 内核,一个容量很大的普通硬盘来存储其他数据。
|
||||
|
||||
在这篇文章中,我将阐述如何通过分离 Root 目录和 Home 目录安装 Ubuntu 系统 — Root 目录存于 SSD(固态硬盘)中,Home 目录存于普通硬盘中。
|
||||
|
||||
### 没有多余的硬盘驱动器?尝试一下 SD 卡(内存卡)!
|
||||
|
||||
![ubuntu-sd-card](https://maketecheasier-2d0f.kxcdn.com/assets/uploads/2017/02/ubuntu-sd-card.jpg "ubuntu-sd-card")
|
||||
![ubuntu-sd-card](https://maketecheasier-2d0f.kxcdn.com/assets/uploads/2017/02/ubuntu-sd-card.jpg "ubuntu-sd-card")
|
||||
|
||||
进行多驱动 Linux 系统安装是很不错的,并且每一个高级用户都应该学会这样做。然而,还有一种情况使得用户应该这样安装 Linux 系统 - 在低存储容量的笔记本电脑上安装系统。可能你有一台很便宜、没有花费太多的笔记本电脑,上面安装了 Linux 系统,电脑上没有多余的硬盘驱动,但有一个 SD 卡插槽。
|
||||
在多个驱动器上安装 Linux 系统是很不错的,并且每一个高级用户都应该学会这样做。然而,还有一种情况使得用户应该这样安装 Linux 系统 - 在低存储容量的笔记本电脑上安装系统。可能你有一台很便宜、没有花费太多的笔记本电脑,上面安装了 Linux 系统,电脑上没有多余的硬盘驱动,但有一个 SD 卡插槽。
|
||||
|
||||
这篇教程也是针对这种类型的电脑的。跟随这篇教程,可以为笔记本电脑买一个高速的 SD 卡来存储 Home 目录,而不是使用另一个硬盘驱动。本教程也适用于这种使用情况。
|
||||
|
||||
### 制作 USB 启动盘
|
||||
|
||||
首先去[网站][11]下载最新的 Ubuntu Linux 版本。然后下载 [Etcher][12]- USB 镜像制作工具。这是一个使用起来很简单的工具,并且支持所有主流的操作系统。你还需要一个至少有 2GB 大小的 USB 驱动器。
|
||||
首先去[这个网站][11]下载最新的 Ubuntu Linux 版本。然后下载 [Etcher][12]- USB 镜像制作工具。这是一个使用起来很简单的工具,并且支持所有主流的操作系统。你还需要一个至少有 2GB 大小的 USB 驱动器。
|
||||
|
||||
![ubuntu-browse-for-ubuntu-iso](https://maketecheasier-2d0f.kxcdn.com/assets/uploads/2017/02/ubuntu-browse-for-ubuntu-iso.jpg "ubuntu-browse-for-ubuntu-iso")
|
||||
![ubuntu-browse-for-ubuntu-iso](https://maketecheasier-2d0f.kxcdn.com/assets/uploads/2017/02/ubuntu-browse-for-ubuntu-iso.jpg "ubuntu-browse-for-ubuntu-iso")
|
||||
|
||||
安装好 Etcher 以后,直接打开。点击 <ruby>选择镜像<rt>Select Image</rt></ruby> 按钮来制作镜像。这将提示用户浏览、寻找 ISO 镜像,找到前面下载的 Ubuntu ISO 文件并选择。然后,插入 USB 驱动器,Etcher 应该会自动选择它。之后,点击 “Flash!” 按钮,Ubuntu 启动盘的制作过程就开始了。
|
||||
|
||||
为了能够启动 Ubuntu 系统,需要配置 BIOS。这是必需的,这样计算机才能启动新创建的 Ubuntu 启动盘。为了进入 BIOS,在插入 USB 的情况下重启电脑,然后按正确的键(Del、F2 或者任何和你的特定电脑匹配的键)。找到从 USB 启动的选项,然后启用这个选项。
|
||||
为了能够启动 Ubuntu 系统,需要配置 BIOS。这是必需的,这样计算机才能启动新创建的 Ubuntu 启动盘。为了进入 BIOS,在插入 USB 的情况下重启电脑,然后按正确的键(Del、F2 或者任何和你的电脑相应的键)。找到从 USB 启动的选项,然后启用这个选项。
|
||||
|
||||
如果你的个人电脑不支持 USB 启动,那么把 Ubuntu 镜像刻入 DVD 中。
|
||||
|
||||
### 安装
|
||||
|
||||
第一次加载 Ubuntu 时,欢迎界面会出现两个选项。请选择 “安装 Ubuntu” 选项。在下一页中,Ubiquity 安装工具会请求用户选择一些选项。这些选项不是强制性的,可以忽略。然而,建议两个选项都勾选,因为这样可以节省安装系统以后的时间,特别是安装 MP3 解码器和更新系统。
|
||||
当用启动盘第一次加载 Ubuntu 时,欢迎界面会出现两个选项。请选择 “安装 Ubuntu” 选项。在下一页中,Ubiquity 安装工具会请求用户选择一些选项。这些选项不是强制性的,可以忽略。然而,建议两个选项都勾选,因为这样可以节省安装系统以后的时间,特别是安装 MP3 解码器和更新系统。(LCTT 译注:当然如果你的网速不够快,还是不要勾选的好。)
|
||||
|
||||
![ubuntu-preparing-to-install](https://maketecheasier-2d0f.kxcdn.com/assets/uploads/2017/02/ubuntu-preparing-to-install.jpg "ubuntu-preparing-to-install")
|
||||
![ubuntu-preparing-to-install](https://maketecheasier-2d0f.kxcdn.com/assets/uploads/2017/02/ubuntu-preparing-to-install.jpg "ubuntu-preparing-to-install")
|
||||
|
||||
勾选了<ruby>“准备安装 Ubuntu”<rt>Preparing to install Ubuntu</rt></ruby>页面中的两个选项以后,需要选择安装类型了。有许多种安装类型。然而,这个教程需要选择自定义安装类型。为了进入自定义安装页面,勾选<ruby>“其他”<rt>something else</rt></ruby>选项,然后点击“继续”。
|
||||
|
||||
现在将显示 Ubuntu 自定义安装分区工具。它将显示任何/所有能够安装 Ubuntu 系统的磁盘。如果两个硬盘均可用,那么它们都会显示。如果插有 SD 卡,那么它也会显示。
|
||||
|
||||
选择用于 Root 文件系统的硬盘驱动器。如果上面已经有分区表,编辑器会显示出来,请使用分区工具把它们全部删除。如果驱动没有格式化也没有分区,那么使用鼠标选择驱动器,然后点击<ruby>“新建分区表”<rt>new partition table</rt></ruby>。对所有驱动器执行这个操作,从而使它们都有分区表。
|
||||
选择用于 Root 文件系统的硬盘驱动器。如果上面已经有分区表,编辑器会显示出来,请使用分区工具把它们全部删除。如果驱动没有格式化也没有分区,那么使用鼠标选择驱动器,然后点击<ruby>“新建分区表”<rt>new partition table</rt></ruby>。对所有驱动器执行这个操作,从而使它们都有分区表。(LCTT 译注:警告,如果驱动器上有你需要的数据,请先备份,否则重新分区后将永远丢失。)
|
||||
|
||||
![ubuntu-create-mount-point](https://maketecheasier-2d0f.kxcdn.com/assets/uploads/2017/02/ubuntu-create-mount-point.jpg "ubuntu-create-mount-point")
|
||||
|
||||
现在所有分区都有了分区表(和已删除分区),可以开始进行配置了。在第一个驱动器下选择空闲空间,然后点击加号按钮来创建新分区。然后将会出现一个“创建分区窗口”。允许工具使用整个硬盘。然后转到<ruby>“挂载点”<rt>Mount Point</rt></ruby>下拉菜单。选择 `/` 作为挂载点,之后点击 OK 按钮确认设置。
|
||||
现在所有分区都有了分区表(并已删除分区),可以开始进行配置了。在第一个驱动器下选择空闲空间,然后点击加号按钮来创建新分区。然后将会出现一个“创建分区窗口”。允许工具使用整个硬盘。然后转到<ruby>“挂载点”<rt>Mount Point</rt></ruby>下拉菜单。选择 `/` (Root)作为挂载点,之后点击 OK 按钮确认设置。
|
||||
|
||||
对第二个驱动器做相同的事,这次选择 `/home` 作为挂载点。两个驱动都设置好以后,选择引导装载器将进入的正确驱动器,然后点击 <ruby>“现在安装”<rt>install now</rt></ruby>,安装进程就开始了。
|
||||
对第二个驱动器做相同的事,这次选择 `/home` 作为挂载点。两个驱动都设置好以后,选择要放入引导装载器的驱动器,然后点击 <ruby>“现在安装”<rt>install now</rt></ruby>,安装进程就开始了。
|
||||
|
||||
![ubuntu-multi-drive-layout](https://maketecheasier-2d0f.kxcdn.com/assets/uploads/2017/02/ubuntu-multi-drive-layout.jpg "ubuntu-multi-drive-layout")
|
||||
|
@ -0,0 +1,120 @@
|
||||
如何从 Vim 中访问 shell 或者运行外部命令
|
||||
============================================================
|
||||
|
||||
Vim——正如你可能已经了解的那样——是一个包含很多特性的强大的编辑器。我们已经写了好多关于 Vim 的教程,覆盖了 [基本用法][4]、 [插件][5], 还有一些 [其他的][6] [有用的][7] 特性。鉴于 Vim 提供了多如海洋的特性,我们总能找到一些有用的东西来和我们的读者分享。
|
||||
|
||||
在这篇教程中,我们将会重点关注你如何在编辑窗口执行外部的命令,并且访问命令行 shell。
|
||||
|
||||
但是在我们开始之前,很有必要提醒一下,在这篇教程中提及到的所有例子、命令行和说明,我们已经在 Ubuntu 14.04 上测试过,我们使用的的 Vim 版本是 7.4 。
|
||||
|
||||
### 在 Vim 中执行外部命令
|
||||
|
||||
有的时候,你可能需要在 Vim 编辑窗口中执行外部的命令。例如,想象一下这种场景:你已经在 Vim 中打开了一个文件,并做了一些修改,然后等你尝试保存这些修改的时候,Vim 抛出一个错误说你没有足够的权限。
|
||||
|
||||
[
|
||||
![在 Vim 中执行命令行](https://www.howtoforge.com/images/how-to-access-shell-or-run-external-commands-from-within-vim/vim-perm-error.png)
|
||||
][8]
|
||||
|
||||
现在,退出当前的 vim 会话,重新使用足够的权限打开文件将意味着你会丢失所做的所有修改,所以,你可能赞同,在大多数情况不是只有一个选择。像这样的情况,在编辑器内部运行外部命令的能力将会派上用场。
|
||||
|
||||
稍后我们再回来上面的用例,但是现在,让我们了解下如何在 vim 中运行基本的命令。
|
||||
|
||||
假设你在编辑一个文件,希望知道这个文件包含的行数、单词数和字符数。为了达到这个目的,在 vim 的命令行模式下,只需要输入冒号 `:`,接下来一个感叹号 `!`,最后是要执行的命令(这个例子中使用的是 `wc`)和紧接着的文件名(使用 `%` 表示当前文件)。
|
||||
|
||||
```
|
||||
:! wc %
|
||||
```
|
||||
|
||||
这是一个例子:
|
||||
|
||||
填入的上面提及的命令行准备执行:
|
||||
|
||||
[
|
||||
![命令准备在 vim 中执行](https://www.howtoforge.com/images/how-to-access-shell-or-run-external-commands-from-within-vim/vim-count-lines.png)
|
||||
][9]
|
||||
|
||||
下面是终端上的输出:
|
||||
|
||||
[
|
||||
![命令输出](https://www.howtoforge.com/images/how-to-access-shell-or-run-external-commands-from-within-vim/vim-wc-output.png)
|
||||
][10]
|
||||
|
||||
在你看到输出之后,输入回车键,你将会退回到你的 vim 会话中。
|
||||
|
||||
你正在编写代码或者脚本,并且希望尽快知道这段代码或者脚本是否包含编译时错误或者语法错误,这个时候,这种特性真的很方便。
|
||||
|
||||
继续,如果需求是添加输出到文件中,使用 `:read !` 命令。接下来是一个例子:
|
||||
|
||||
```
|
||||
:read ! wc %
|
||||
```
|
||||
|
||||
`read` 命令会把外部命令的输出作为新的一行插入到编辑的文件中的当前行的下面一行。如果你愿意,你也可以指定一个特定的行号——输出将会添加到特定行之后。
|
||||
|
||||
例如,下面的命令将会在文件的第二行之后添加 `wc` 的输出。
|
||||
|
||||
```
|
||||
:2read ! wc %
|
||||
```
|
||||
|
||||
**注意**: 使用 `$` 在最后一行插入, `0` 在第一行前面插入。
|
||||
|
||||
现在,回到最开始我们讨论的一个用例,下面的命令将会帮助你保存文件而不需要先关闭文件(这将意味着没有保存的内容不会丢失)然后使用 [sudo][11] 命令重新打开。
|
||||
|
||||
```
|
||||
:w ! sudo tee %
|
||||
```
|
||||
|
||||
[
|
||||
![](https://www.howtoforge.com/images/how-to-access-shell-or-run-external-commands-from-within-vim/vim-sudo-passwrd.png)
|
||||
][12]
|
||||
|
||||
### 在 Vim 中访问 shell
|
||||
|
||||
除了可以执行单独的命令,你也可以在 vim 中放入自己新创建的 shell。为了达到这种目的,在编辑器中你必须要做的是运行以下的命令:
|
||||
|
||||
```
|
||||
:shell
|
||||
```
|
||||
|
||||
或者:
|
||||
|
||||
```
|
||||
:sh
|
||||
```
|
||||
|
||||
当你执行完了你的 shell 任务,输入 `exit` —— 这将带你回到原来离开的 Vim 会话中。
|
||||
|
||||
### 要谨记的漏洞
|
||||
|
||||
虽然在真实世界中,能够访问的 shell 绝对符合它们的用户权限,但是它也可以被用于提权技术。正如我们在早期的一篇文章(在 sudoedit 上)解释的那样,即使你提供给一个用户 `sudo` 的权限只是通过 Vim 编辑一个文件,他们仍可以使用这项技术从编辑器中运行一个新的 shell,而且他们可以做 `root` 用户或者管理员用户可以做的所有内容。
|
||||
|
||||
### 总结
|
||||
|
||||
能够在 Vim 中运行外部命令在好多场景中(有些场景我们已经在这篇文章中提及了)都是一个很有用的特性。这个功能的学习曲线并不麻烦,所以初学者和有经验的用户都可以好好使用它。
|
||||
|
||||
你现在使用这个特性有一段时间了吗?你是否有一些东西想分享呢?请在下面的评论中留下你的想法。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.howtoforge.com/tutorial/how-to-access-shell-or-run-external-commands-from-within-vim/
|
||||
|
||||
作者:[Himanshu Arora][a]
|
||||
译者:[yangmingming](https://github.com/yangmingming)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:https://www.howtoforge.com/tutorial/how-to-access-shell-or-run-external-commands-from-within-vim/
|
||||
[1]:https://www.howtoforge.com/tutorial/how-to-access-shell-or-run-external-commands-from-within-vim/#execute-external-commands-in-vim
|
||||
[2]:https://www.howtoforge.com/tutorial/how-to-access-shell-or-run-external-commands-from-within-vim/#access-shell-in-vim
|
||||
[3]:https://www.howtoforge.com/tutorial/how-to-access-shell-or-run-external-commands-from-within-vim/#the-loophole-to-keep-in-mind
|
||||
[4]:https://www.howtoforge.com/vim-basics
|
||||
[5]:https://www.howtoforge.com/tutorial/vim-editor-plugins-for-software-developers-3/
|
||||
[6]:https://www.howtoforge.com/tutorial/vim-modeline-settings/
|
||||
[7]:https://www.howtoforge.com/tutorial/vim-editor-modes-explained/
|
||||
[8]:https://www.howtoforge.com/images/how-to-access-shell-or-run-external-commands-from-within-vim/big/vim-perm-error.png
|
||||
[9]:https://www.howtoforge.com/images/how-to-access-shell-or-run-external-commands-from-within-vim/big/vim-count-lines.png
|
||||
[10]:https://www.howtoforge.com/images/how-to-access-shell-or-run-external-commands-from-within-vim/big/vim-wc-output.png
|
||||
[11]:https://www.howtoforge.com/tutorial/sudo-beginners-guide/
|
||||
[12]:https://www.howtoforge.com/images/how-to-access-shell-or-run-external-commands-from-within-vim/big/vim-sudo-passwrd.png
|
46
published/20170317 The End of the Line for EPEL-5.md
Normal file
46
published/20170317 The End of the Line for EPEL-5.md
Normal file
@ -0,0 +1,46 @@
|
||||
EPEL-5 走向终点
|
||||
===========
|
||||
|
||||
![](https://cdn.fedoramagazine.org/wp-content/uploads/2017/03/epel5-eol-945x400.png)
|
||||
|
||||
在过去十年中,Fedora 项目一直都在为另外一个操作系统构建相同软件包。**然而,到 2017 年 3 月 31 日,它将会随着 Red Hat Enterprise Linux(RHEL)5 一起停止这项工作**。
|
||||
|
||||
### EPEL 的简短历史
|
||||
|
||||
RHEL 是 Fedora 发布版本的一个子集的下游重建版本,Red Hat 愿为之支持好多年。虽然那些软件包构成了完整的操作系统,但系统管理员一直都需要“更多”软件包。在 RHEL-5 之前,许多那些软件包会由不同的人打包并提供。随着 Fedora Extras 逐渐包含了更多软件包,并有几位打包者加入了 Fedora,随之出现了一个想法,结合力量并创建一个专门的子项目,重建特定于 RHEL 版本的 Fedora 软件包,然后从 Fedora 的中心化服务器上分发。
|
||||
|
||||
经过多次讨论,然而还是未能提出一个引人注目的名称之后,Fedora 创建了子项目 Extra Packages for Enterprise Linux(简称 EPEL)。在首次为 RHEL-4 重建软件包时,其主要目标是在 RHEL-5 发布时提供尽可能多的用于 RHEL-5 的软件包。打包者做了很多艰苦的工作,但大部分工作是在制定 EPEL 在未来十年的规则以及指导。[从所有人能够看到的邮件归档中][2]我们可以看到 Fedora 贡献者的激烈讨论,它们担心将 Fedora 的发布重心转移到外部贡献者会与已经存在的软件包产生冲突。
|
||||
|
||||
最后,EPEL-5 在 2007 年 4 月的某个时候上线了,在接下来的十年中,它已经成长为一个拥有 5000 多个源码包的仓库,并且每天会有 20 万个左右独立 IP 地址检查软件包,并在 2013 年初达到 24 万的高峰。虽然为 EPEL 构建的每个包都是使用 RHEL 软件包完成的,但所有这些软件包可以用于 RHEL 的各种社区重建版本(CentOS、Scientific Linux、Amazon Linux)。这意味着随着这些生态系统的增长,给 EPEL 带来了更多的用户,并在随后的 RHEL 版本发布时帮助打包。然而,随着新版本以及重建版本的使用量越来越多,EPEL-5 的用户数量逐渐下降为每天大约 16 万个独立 IP 地址。此外,在此期间,开发人员支持的软件包数量已经下降,仓库大小已缩小到 2000 个源代码包。
|
||||
|
||||
收缩的部分原因是由于 2007 年的原始规定。当时,Red Hat Enterprise Linux 被认为只有 6 年活跃的生命周期。有人认为,在这样一个“有限”的周期中,软件包可能就像在 RHEL 中那样在 EPEL 中被“冻结”。这意味着无论何时有可能的修复需要向后移植,也不允许有主要的修改。因为没有人来打包,软件包将不断从 EPEL-5 中移除,因为打包者不再想尝试并向后移植。尽管各种规则被放宽以允许更大的更改,Fedora 使用的打包规则从 2007 年开始不断地改变和改进。这使得在较旧的操作系统上尝试重新打包一个较新的版本变得越来越难。
|
||||
|
||||
### 2017 年 3 月 31 日会发生什么
|
||||
|
||||
如上所述,3 月 31 日,红帽将终止 RHEL-5 的支持并不再为普通客户提供更新。这意味着 Fedora 和各种重建版本将开始各种归档流程。对于 EPEL 项目,这意味着我们将跟随 Fedora 发行版每年发布的步骤。
|
||||
|
||||
1. 在 ** 3 月 27 日**,任何新版本将不会被允许推送到 EPEL-5,以便仓库本质上被冻结。这允许镜像拥有一个清晰的文件树。
|
||||
2. EPEL-5 中的所有包将从主镜像 `/pub/epel/5/` 以及 `/pub/epel/testing/5/` 移动到 `/pub/archives/epel/`。 **这将会在 27 号开始*,因此所有的归档镜像站点可以用它写入磁盘。
|
||||
3. 因为 3 月 31 日是星期五,系统管理员并不喜欢周五惊喜,所以它不会有变化。**4 月 3 日**,镜像管理器将更新指向归档。
|
||||
4. **4 月 6 日**,`/pub/epel/5/` 树将被删除,镜像也将相应更新。
|
||||
|
||||
对于使用 cron 执行 yum 更新的系统管理员而言,这应该只是一个小麻烦。系统能继续更新甚至安装归档中的任何软件包。那些直接使用脚本从镜像下载的系统管理员会有点麻烦,需要将脚本更改到 `/pub/archive/epel/5/` 这个新的位置。
|
||||
|
||||
虽然令人讨厌,但是对于仍使用旧版 Linux 的许多系统管理员也许算是好事吧。由于软件包不断地从 EPEL-5 中删除,各种支持邮件列表以及 irc 频道都有系统管理员惊奇他们需要的哪些软件包消失到哪里了。归档完成后,这将不会是一个问题,因为不会更多的包会被删除了 :)。
|
||||
|
||||
对于受此问题影响的系统管理员,较旧的 EPEL 软件包仍然可用,但速度较慢。所有 EPEL 软件包都是在 Fedora Koji 系统中构建的,所以你可以使用 [Koji 搜索][3]到较旧版本的软件包。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://fedoramagazine.org/the-end-of-the-line-for-epel-5/
|
||||
|
||||
作者:[smooge][a]
|
||||
译者:[geekpi](https://github.com/geekpi)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:http://smooge.id.fedoraproject.org/
|
||||
[1]:https://fedoramagazine.org/the-end-of-the-line-for-epel-5/
|
||||
[2]:https://www.redhat.com/archives/epel-devel-list/2007-March/thread.html
|
||||
[3]:https://koji.fedoraproject.org/koji/search
|
@ -1,77 +0,0 @@
|
||||
Translating by SysTick
|
||||
|
||||
The impact GitHub is having on your software career
|
||||
============================================================
|
||||
|
||||
|
||||
![The impact GitHub is having on your software career](https://opensource.com/sites/default/files/styles/image-full-size/public/images/life/github-universe.jpg?itok=HCU81VX8 "The impact GitHub is having on your software career")
|
||||
>Image credits : From GitHub
|
||||
|
||||
Over the next 12 to 24 months (in other words, between 2018 and 2019), how people hire software developers will change radically.
|
||||
|
||||
I spent from 2004 to 2014 working at Red Hat, the world's largest open source software engineering company. On my very first day there, in July 2004, my boss Marty Messer said to me, "All the work you do here will be in the open. In the future, you won't have a CV—people will just Google you."
|
||||
|
||||
This was one of the unique characteristics of working at Red Hat at the time. We had the opportunity to create our own personal brands and reputation in the open. Communication with other software engineers through mailing lists and bug trackers, and source code commits to mercurial, subversion, and CVS (Concurrent Versions System) repositories were all open and indexed by Google.
|
||||
|
||||
Fast-forward to 2017, and here we are living in a world that is being eaten by open source software.
|
||||
|
||||
There are two factors that give you a real sense of the times:
|
||||
|
||||
1. Microsoft, long the poster child for closed-source proprietary software and a crusader against open source, has embraced open source software whole-heartedly. The company formed the .NET Foundation (which has Red Hat as a member) and joined the Linux Foundation. .NET is now developed in the open as an open source project.
|
||||
2. GitHub has become a singular social network that ties together issue tracking and distributed source control.
|
||||
|
||||
For software developers coming from a primarily closed source background, it's not really clear yet what just happened. To them, open source equals "working for free in your spare time."
|
||||
|
||||
For those of us who spent the past decade making a billion-dollar open source software company, however, there is nothing free or spare time about working in the open. Also, the benefits and consequences of working in the open are clear, your reputation is yours and is portable between companies. GitHub is a social network where your social capital, created by your commits and contribution to the global conversation in whatever technology you are working, is yours—not tied to the company you happen to be working at temporarily.
|
||||
|
||||
Smart people will take advantage of this environment. They'll contribute patches, issues, and comments upstream to the languages and frameworks that they use daily in their job, including TypeScript, .NET, and Redux. They'll also advocate for and creatively arrange for as much of their work as possible to be done in the open, even if it is just their contribution graph to private repositories.
|
||||
|
||||
GitHub is a great equalizer. You may not be able to get a job in Australia from India, but there is nothing stopping you from working with Australians on GitHub from India.
|
||||
|
||||
The way to get a job at Red Hat during the last decade was obvious. You just started collaborating with Red Hat engineers on a piece of technology that they were working on in the open, then when it was clear that you were making a valuable contribution and were a great person to work with, you would apply for a job. (Or they would hit you up.)
|
||||
|
||||
Now that same pathway is open for everyone, into just about any technology. As the world is eaten by open source, the same dynamic is now prevalent everywhere.
|
||||
|
||||
In [a recent interview][3], Linus Torvalds (49K followers, following 0 on GitHub), the inventor of Linux and git, put it like this, "You shoot off a lot of small patches until the point where the maintainers trust you, and at that point you become more than just a guy who sends patches, you become part of the network of trust."
|
||||
|
||||
Your reputation is your location in a network of trust. When you change companies, this is weakened and some of it is lost. If you live in a small town and have been there for a long time, then people all over town know you. However, if you move countries, then that goes. You end up somewhere where no one knows you—and worse, no one knows anyone who knows you.
|
||||
|
||||
You've lost your first- and second-, and probably even third-degree connections. Unless you've built a brand by speaking at conferences or some other big ticket event, the trust you built up by working with others and committing code to a corporate internal repository is gone. However, if that work has been on GitHub, it's not gone. It's visible. It's connected to a network of trust that is visible.
|
||||
|
||||
One of the first things that will happen is that the disadvantaged will start to take advantage of this. Students, new grads, immigrants—they'll use this to move to Australia.
|
||||
|
||||
This will change the landscape. Previously privileged developers will suddenly find their network disrupted. One of the principles of open source is meritocracy—the best idea wins, the most commits wins, the most passing tests wins, the best implementation wins, etc.
|
||||
|
||||
It's not perfect, nothing is, and it doesn't do away with or discount being a good person to work with. Companies fire some rockstar engineers who just don't play well with others, and that stuff does show up in GitHub, mostly in the interactions with other contributors.
|
||||
|
||||
GitHub is not simply a code repository and a list of raw commit numbers, as some people paint it in strawman arguments. It is a social network. I put it like this: It's not your code on GitHub that counts; it's what other people say on GitHub about your code that counts.
|
||||
|
||||
GitHub is your portable reputation, and over the next 12 to 24 months, as some developers develop that and others don't, it's going to be a stark differentiator. It's like having email versus not having email (and now everyone has email), or having a cell phone versus not having a cell phone (and now everyone has a cell phone). Eventually, a vast majority will be working in the open, and it will again be a level playing field differentiated on other factors.
|
||||
|
||||
But right now, the developer career space is being disrupted by GitHub.
|
||||
|
||||
_[This article][1] originally appeared on Medium.com. Reprinted with permission._
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
作者简介:
|
||||
|
||||
Josh Wulf - About me: I'm a Legendary Recruiter at Just Digital People; a Red Hat alumnus; a CoderDojo mentor; a founder of Magikcraft.io; the producer of The JDP Internship — The World's #1 Software Development Reality Show;
|
||||
|
||||
-----------------------
|
||||
|
||||
via: https://opensource.com/article/17/3/impact-github-software-career
|
||||
|
||||
作者:[Josh Wulf ][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/sitapati
|
||||
[1]:https://medium.com/@sitapati/the-impact-github-is-having-on-your-software-career-right-now-6ce536ec0b50#.dl79wpyww
|
||||
[2]:https://opensource.com/article/17/3/impact-github-software-career?rate=2gi7BrUHIADt4TWXO2noerSjzw18mLVZx56jwnExHqk
|
||||
[3]:http://www.theregister.co.uk/2017/02/15/think_different_shut_up_and_work_harder_says_linus_torvalds/
|
||||
[4]:https://opensource.com/user/118851/feed
|
||||
[5]:https://opensource.com/article/17/3/impact-github-software-career#comments
|
||||
[6]:https://opensource.com/users/sitapati
|
111
sources/talk/20170314 One Year Using Go.md
Normal file
111
sources/talk/20170314 One Year Using Go.md
Normal file
@ -0,0 +1,111 @@
|
||||
[One Year Using Go][18]
|
||||
============================================================
|
||||
|
||||
![](https://bugfender.com/wp-content/uploads/2017/03/one-year-using-go-social-1.jpg)
|
||||
|
||||
Our ventures into [Go][5] all started as an internal experiment at [Mobile Jazz][6]. As the company name hints, we develop mobile apps.
|
||||
|
||||
After releasing an app into the wild, we soon realised we were missing a tool to check what was actually happening to users and how they were interacting with the app – something that would have been very handy in the case of any issues or bugs being reported.
|
||||
|
||||
There were a couple of tools around which claimed to help developers in this area, but none of them quite hit the mark, so we decided to build our own. We started by creating a basic set of scripts that quickly evolved into a fully-fledged tool known today as [Bugfender][7]!
|
||||
|
||||
As this was initially an experiment, we decided to try out a new trending technology. A love of learning and continual education is a key aspect of Mobile Jazz’s core values, so we decided to build it using Go; a relatively new programming language developed by Google. It’s a new player in the game and there have been many respected developers saying great things about it.
|
||||
|
||||
One year later and the experiment has turned into a startup, we’ve got an incredible tool that’s already helping thousands of developers all over the world. Our servers are processing over 200GB of data everyday incoming from more than 7 million devices.
|
||||
|
||||
After using Go for a year, we’d like to share some of our thoughts and experiences from taking our small experiment to a production server handling millions and millions of logs.
|
||||
|
||||
### Go Ecosystem
|
||||
|
||||
No one in the company had any previous experience using Go. Bugfender was our first dive into the language.
|
||||
|
||||
Learning the basics was pretty straight forward. Our previous experiences with C/C++/Java/Objective-C/PHP enabled us to learn Go quickly and get developing in days. There were, of course, a few new and unusual things to learn, including GOPATH and how to deal with packages, but that was expected.
|
||||
|
||||
Within a few days, we realized that even being a simplified language by design, Go was extremely powerful. It was able to do everything a modern programming language should: being able to work with JSON, communicate between servers and even access databases with no problems (and with just a few lines of code at that).
|
||||
|
||||
When building a server, you should first decide if you’re going to use any third party libraries or frameworks. For Bugfender, we decided to use:
|
||||
|
||||
### Martini
|
||||
|
||||
[Martini][8] is a powerful web framework for Go. At the time we started the experiment, it was a great solution and to this day, we haven’t experienced any problems with it. However if we were to start this experiment again today, we would choose a different framework as Martini is no longer maintained.
|
||||
|
||||
We’ve further experimented with [Iris][9] (our current favorite) and [Gin][10]. Gin is the successor to Martini and migrating to this will enable us to reuse our existing code.
|
||||
|
||||
In the past year, we’ve realized that Go’s standard libraries are really powerful and that you don’t really need to rely on a heavy web framework to build a server. It is better to use high-performance libraries that specialize in specific tasks.
|
||||
|
||||
~~Iris is our current ~~favourite~~ and in the future, we’ll re-write our servers to use it instead of Martini/Gin, but it’s not a priority right now.~~
|
||||
|
||||
**Edit:** After some discussions about Iris in differents places, we realized that Iris might not be the best option. If we ever decide to re-write our web components, we might look into other options, we are open to suggestions.
|
||||
|
||||
### Gorm
|
||||
|
||||
Some people are fans of ORM and others are not. We decided to use ORM and more specifically, [GORM][11]. Our implementation was for the web frontend only, keeping it optimized with hand-written SQL for the log ingestion API. In the beginning, we were really happy, but as time progresses, we’ve started to find problems and we are soon going to remove it completely from our code and use a lower level approach using a standard SQL library with [sqlx][12].
|
||||
|
||||
One of the main problems with GORM is Go’s ecosystem. As a new language, there have been many new versions since we started developing the product. Some changes in these new releases are not backwards compatible and so, to use the newest library versions we are frequently rewriting existing code and checking hacks we may have created to solve version issues.
|
||||
|
||||
These two libraries are the main building blocks of almost any web server, so it’s important to make a good choice because it can be difficult to change later and will affect your server performance.
|
||||
|
||||
### Third-Party Services
|
||||
|
||||
Another important area to consider when creating a real world product is the availability of libraries, third-party services and tools. Here, Go is still lacking maturity, most companies don’t yet provide a Go library, so you may need to rely on libraries written by other people where quality isn’t always guaranteed.
|
||||
|
||||
For example, there are great libraries for using [Redis][13] and [ElasticSearch][14], but libraries for other services such as Mixpanel or Stripe are not so good.
|
||||
|
||||
Our recommendation before using Go is to check beforehand if there’s a good library available for any specific products you may need.
|
||||
|
||||
We’ve also experienced a lot of problems with Go’s package management system. The way it handles versions is far from optimal and over the past year, we have run into various problems getting different versions of the same library between different team members. Recently, however, this problem has been almost solved thanks to the new Go feature that supports vendor packages, alongside the [gopkg.in][15] service.
|
||||
|
||||
### Developer Tools
|
||||
|
||||
![](https://bugfender.com/wp-content/uploads/2017/03/go-ide.jpg)
|
||||
|
||||
As Go is a relatively new language, you might find the developer tools available are not so great when compared to other established languages like Java. When we started Bugfender it was really hard to use any IDE, none seemed to support Go. But in this last year, this has improved a lot with the introduction of [IntelliJ][16] and [Visual Studio Code Go][17] plugins.
|
||||
|
||||
Finally looking at other Go tools, the debugger is not-so-great and the profiler is even worse, so debugging your code or trying to optimize it can be hard at times.
|
||||
|
||||
### Heading for Production
|
||||
|
||||
This is definitely one of the best things about Go, if you want to deploy something to production you just need to build your binary and send it to the server, no dependencies, no need to install extra software, you only need to be able to run a binary file in your server.
|
||||
|
||||
If you’re used to dealing with other languages where you require a package manager or need to be careful with a language interpreter you may use, Go is a pleasure to work with.
|
||||
|
||||
We are also really happy with Go’s stability as the servers never seem to crash. We faced a problem some time ago sending big amounts of data to Go Routines but since then we’ve rarely seen any crashes. Note: if you need to send a lot of data to a Go Routine, you’ll need to be careful as you can have a heap overflow.
|
||||
|
||||
If you’re interested in performance, we cannot compare to other languages as we have started with Go from scratch, but given the amount of data we process, we feel it’s performance is very good, we definitely wouldn’t be able to process the same number of requests using PHP so easily.
|
||||
|
||||
### Conclusions
|
||||
|
||||
Over the year we’ve had our ups and downs regarding Go. At the beginning we were excited, but after the experiment converted to a real product we started unveiling problems. We’ve thought several times about a complete rewrite in Java, but here we are, still working with Go, and in this past year the ecosystem has had some great improvements that simplified our work.
|
||||
|
||||
If you want to build your product using Go, you can be sure it will work, but you need to be really careful with one thing: the availability of developers to hire. There are only a few senior Go developers in Silicon Valley, and finding one elsewhere can be a very difficult task.
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://bugfender.com/one-year-using-go
|
||||
|
||||
作者:[ALEIX VENTAYOL][a]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:https://bugfender.com/author/aleixventayol
|
||||
[1]:https://bugfender.com/#facebook
|
||||
[2]:https://bugfender.com/#twitter
|
||||
[3]:https://bugfender.com/#google_plus
|
||||
[4]:https://www.addtoany.com/share#url=https%3A%2F%2Fbugfender.com%2Fone-year-using-go&title=One%20Year%20Using%20Go
|
||||
[5]:https://golang.org/
|
||||
[6]:http://mobilejazz.com/
|
||||
[7]:https://www.bugfender.com/
|
||||
[8]:https://github.com/go-martini/martini
|
||||
[9]:https://github.com/kataras/iris
|
||||
[10]:https://github.com/gin-gonic/gin
|
||||
[11]:https://github.com/jinzhu/gorm
|
||||
[12]:https://github.com/jmoiron/sqlx
|
||||
[13]:https://github.com/go-redis/redis
|
||||
[14]:https://github.com/olivere/elastic
|
||||
[15]:http://labix.org/gopkg.in
|
||||
[16]:https://plugins.jetbrains.com/plugin/5047-go
|
||||
[17]:https://github.com/Microsoft/vscode-go
|
||||
[18]:https://bugfender.com/one-year-using-go
|
@ -0,0 +1,218 @@
|
||||
Linux on UEFI:A Quick Installation Guide
|
||||
============================================================
|
||||
|
||||
|
||||
This Web page is provided free of charge and with no annoying outside ads; however, I did take time to prepare it, and Web hosting does cost money. If you find this Web page useful, please consider making a small donation to help keep this site up and running. Thanks!
|
||||
|
||||
### Introduction
|
||||
|
||||
For several years, a new firmware technology has been lurking in the wings, unknown to most ordinary users. Known as the [Extensible Firmware Interface (EFI),][29] or more recently as the Unified EFI (UEFI, which is essentially EFI 2. _x_ ), this technology has begun replacing the older [Basic Input/Output System (BIOS)][30] firmware with which most experienced computer users are at least somewhat familiar.
|
||||
|
||||
This page is a quick introduction to EFI for Linux users, including advice on getting started installing Linux to such a computer. Unfortunately, EFI is a dense topic; the EFI software itself is complex, and many implementations have system-specific quirks and even bugs. Thus, I cannot describe everything you'll need to know to install and use Linux on an EFI computer on this one page. It's my hope that you'll find this page a useful starting point, though, and links within each section and in the [References][31] section at the end will point you toward additional documentation.
|
||||
|
||||
#### Contents
|
||||
|
||||
* [Introduction][18]
|
||||
* [Does Your Computer Use EFI?][19]
|
||||
* [Does Your Distribution Support EFI?][20]
|
||||
* [Preparing to Install Linux][21]
|
||||
* [Installing Linux][22]
|
||||
* [Fixing Post-Installation Problems][23]
|
||||
* [Oops: Converting a Legacy-Mode Install to Boot in EFI Mode][24]
|
||||
* [References][25]
|
||||
|
||||
### Does Your Computer Use EFI?
|
||||
|
||||
EFI is a type of _firmware,_ meaning that it's software built into the computer to handle low-level tasks. Most importantly, the firmware controls the computer's boot process, which in turn means that EFI-based computers boot differently than do BIOS-based computers. (A partial exception to this rule is described shortly.) This difference can greatly complicate the design of OS installation media, but it has little effect on the day-to-day operation of the computer, once everything is set up and running. Note that most manufacturers use the term "BIOS" to refer to their EFIs. I consider this usage confusing, so I avoid it; in my view, EFIs and BIOSes are two different types of firmware.
|
||||
|
||||
**Note:** The EFI that Apple uses on Macs is unusual in many respects. Although much of this page applies to Macs, some details differ, particularly when it comes to setting up EFI boot loaders. This task is best handled from OS X by using the Mac's [bless utility,][49]which I don't describe here.
|
||||
|
||||
EFI has been used on Intel-based Macs since they were first introduced in 2006\. Beginning in late 2012, most computers that ship with Windows 8 or later boot using UEFI by default, and in fact most PCs released since mid-2011 use UEFI, although they may not boot in EFI mode by default. A few PCs sold prior to 2011 also support EFI, although most such computers boot in BIOS mode by default.
|
||||
|
||||
If you're uncertain about your computer's EFI support status, you should check your firmware setup utility and your user manual for references to _EFI_ , _UEFI_ , or _legacy booting_ . (Searching a PDF of your user manual can be a quick way to do this.) If you find no such references, your computer probably uses an old-style ("legacy") BIOS; but if you find references to these terms, it almost certainly uses EFI. You can also try booting a medium that contains _only_ an EFI-mode boot loader. The USB flash drive or CD-R image of [rEFInd][50] is a good choice for this test.
|
||||
|
||||
Before proceeding further, you should understand that most EFIs on _x_ 86 and _x_ 86-64 computers include a component known as the _Compatibility Support Module (CSM),_ which enables the EFI to boot OSes using the older BIOS-style boot mechanisms. This can be a great convenience because it provides backwards compatibility; but it also creates complications because there's no standardization in the rules and user interfaces for controlling when a computer boots in EFI mode vs. when it boots in BIOS (aka CSM or legacy) mode. In particular, it's far too easy to accidentally boot your Linux installation medium in BIOS/CSM/legacy mode, which will result in a BIOS/CSM/legacy-mode installation of Linux. This can work fine if Linux is your only OS, but it complicates the boot process if you're dual-booting with Windows in EFI mode. (The opposite problem can also occur.) The following sections should help you boot your installer in the right mode. If you're reading this page after you've installed Linux in BIOS mode and want to switch boot modes, read the upcoming section, [Oops: Converting a Legacy-Mode Install to Boot in EFI Mode.][51]
|
||||
|
||||
One optional feature of UEFI deserves mention: _Secure Boot._ This feature is designed to minimize the risk of a computer becoming infected with a _boot kit,_ which is a type of malware that infects the computer's boot loader. Boot kits can be particularly difficult to detect and remove, which makes blocking them a priority. Microsoft requires that all desktop and laptop computers that bear a Windows 8 logo ship with Secure Boot enabled. This type of configuration complicates Linux installation, although some distributions handle this problem better than do others. Do not confuse Secure Boot with EFI or UEFI, though; it's possible for an EFI computer to not support Secure Boot, and it's possible to disable Secure Boot even on _x_ 86-64 EFI computers that support it. Microsoft requires that users can disable Secure Boot for Windows 8 certification on _x_ 86 and _x_ 86-64 computers; however, this requirement is reversed for ARM computers—such computers that ship with Windows 8 must _not_ permit the user to disable Secure Boot. Fortunately, ARM-based Windows 8 computers are currently rare. I recommend avoiding them.
|
||||
|
||||
### Does Your Distribution Support EFI?
|
||||
|
||||
Most Linux distributions have supported EFI for years. The quality of that support varies from one distribution to another, though. Most of the major distributions (Fedora, OpenSUSE, Ubuntu, and so on) provide good EFI support, including support for Secure Boot. Some more "do-it-yourself" distributions, such as Gentoo, have weaker EFI support, but their nature makes it easy to add EFI support to them. In fact, it's possible to add EFI support to _any_ Linux distribution: You need to install it (even in BIOS mode) and then install an EFI boot loader on the computer. See the [Oops: Converting a Legacy-Mode Install to Boot in EFI Mode][52] section for information on how to do this.
|
||||
|
||||
You should check your distribution's feature list to determine if it supports EFI. You should also pay attention to your distribution's support for Secure Boot, particularly if you intend to dual-boot with Windows 8\. Note that even distributions that officially support Secure Boot may require that this feature be disabled, since Linux Secure Boot support is often poor or creates complications.
|
||||
|
||||
### Preparing to Install Linux
|
||||
|
||||
A few preparatory steps will help make your Linux installation on an EFI-based computer go more smoothly:
|
||||
|
||||
1. **Upgrade your firmware**—Some EFIs are badly broken, but hardware manufacturers occasionally release updates to their firmware. Thus, I recommend upgrading your firmware to the latest version available. If you know from forum posts or the like that your EFI is problematic, you should do this before installing Linux, because some problems will require extra steps to correct if the firmware is upgraded after the installation. On the other hand, upgrading firmware is always a bit risky, so holding off on such an upgrade may be best if you've heard good things about your manufacturer's EFI support.
|
||||
3. **Learn how to use your firmware**—You</a> can usually enter a firmware setup utility by hitting the Del key or a function key early in the boot process. Check for prompts soon after you power on the computer or just try each function key. Similarly, the Esc key or a function key usually enters the firmware's built-in boot manager, which enables you to select which OS or external device to boot. Some manufacturers are making it hard to reach such settings. In some cases, you can do so from inside Windows 8, as described on [this page.][32]
|
||||
4. **Adjust the following firmware settings:**
|
||||
* **Fast boot**—This feature can speed up the boot process by taking shortcuts in hardware initialization. Sometimes this is fine, but sometimes it can leave USB hardware uninitialized, which can make it impossible to boot from a USB flash drive or similar device. Thus, disabling fast boot _may_ be helpful, or even required; but you can safely leave it active and deactivate it only if you have trouble getting the Linux installer to boot. Note that this feature sometimes goes by another name. In some cases, you must _enable_ USB support rather than _disable_ a fast boot feature.
|
||||
* **Secure Boot**—Fedora, OpenSUSE, Ubuntu, and some other distributions officially support Secure Boot; but if you have problems getting a boot loader or kernel to start, you might want to disable this feature. Unfortunately, fully describing how to do so is impossible because the settings vary from one computer to another. See [my Secure Boot page][1] for more on this topic.
|
||||
|
||||
**Note:** Some guides say to enable BIOS/CSM/legacy support to install Linux. As a general rule, they're wrong to do so. Enabling this support can overcome hurdles involved in booting the installer, but doing so creates new problems down the line. Guides to install in this way often overcome these later problems by running Boot Repair, but it's better to do it correctly from the start. This page provides tips to help you get your Linux installer to boot in EFI mode, thus bypassing the later problems.
|
||||
|
||||
* **CSM/legacy options**—If you want to install in EFI mode, set such options _off._ Some guides recommend enabling these options, and in some cases they may be required—for instance, they may be needed to enable the BIOS-mode firmware in some add-on video cards. In most cases, though, enabling CSM/legacy support simply increases the risk of inadvertently booting your Linux installer in BIOS mode, which you do _not_ want to do. Note that Secure Boot and CSM/legacy options are sometimes intertwined, so be sure to check each one after changing the other.
|
||||
5. **Disable the Windows Fast Startup feature**—[This page][33] describes how to disable this feature, which is almost certain to cause filesystem corruption if left enabled. Note that this feature is distinct from the firmware's fast boot feature.
|
||||
6. **Check your partition table**—Using [GPT fdisk,][34] parted, or any other partitioning tool, check your disk's partitions. Ideally, you should create a hardcopy that includes the exact start and end points (in sectors) of each partition. This will be a useful reference, particularly if you use a manual partitioning option in the installer. If Windows is already installed, be sure to identify your [EFI System Partition (ESP),][35] which is a FAT partition with its "boot flag" set (in parted or GParted) or that has a type code of EF00 in gdisk.
|
||||
|
||||
### Installing Linux
|
||||
|
||||
Most Linux distributions provide adequate installation instructions; however, I've observed a few common stumbling blocks on EFI-mode installations:
|
||||
|
||||
* **Ensure that you're using a distribution that's the right bit depth**—EFI runs boot loaders that are the same bit depth as the EFI itself. This is normally 64-bit for modern computers, although the first couple generations of Intel-based Macs, some modern tablets and convertibles, and a handful of obscure computers use 32-bit EFIs. I have yet to encounter a 32-bit Linux distribution that officially supports EFI, although it is possible to add a 32-bit EFI boot loader to 32-bit distributions. (My [Managing EFI Boot Loaders for Linux][36] covers boot loaders generally, and understanding those principles may enable you to modify a 32-bit distribution's installer, although that's not a task for a beginner.) Installing a 32-bit Linux distribution on a computer with a 64-bit EFI is difficult at best, and I don't describe the process here; you should use a 64-bit distribution on a computer with a 64-bit EFI.
|
||||
* **Properly prepare your boot medium**—Third-party tools for moving .iso images onto USB flash drives, such as unetbootin, often fail to create the proper EFI-mode boot entries. I recommend you follow whatever procedure your distribution maintainer suggests for creating USB flash drives. If no such recommendation is made, use the Linux dd utility, as in dd if=image.iso of=/dev/sdc to create an image on the USB flash drive on /dev/sdc. Ports of dd to Windows, such as [WinDD][37] and [dd for Windows,][38] exist, but I've never tested them. Note that using tools that don't understand EFI to create your installation medium is one of the mistakes that leads people into the bigger mistake of installing in BIOS mode and then having to correct the ensuing problems, so don't ignore this point!
|
||||
* **Back up the ESP**—If you're installing to a computer that already boots Windows or some other OS, I recommend backing up your ESP before installing Linux. Although Linux _shouldn't_ damage files that are already on the ESP, this does seem to happen from time to time. Having a backup will help in such cases. A simple file-level backup (using cp, tar, or zip, for example) should work fine.
|
||||
* **Booting in EFI mode**—It's too easy to accidentally boot your Linux installer in BIOS/CSM/legacy mode, particularly if you leave the CSM/legacy options enabled in your firmware. A few tips can help you to avoid this problem:
|
||||
|
||||
* You should verify an EFI-mode boot by dropping to a Linux shell and typing ls /sys/firmware/efi. If you see a list of files and directories, you've booted in EFI mode and you can ignore the following additional tips; if not, you've probably booted in BIOS mode and should review your settings.
|
||||
* Use your firmware's built-in boot manager (which you should have located earlier; see [Learn how to use your firmware][26]) to boot in EFI mode. Typically, you'll see two options for a CD-R or USB flash drive, one of which includes the string _EFI_ or _UEFI_ in its description, and one of which does not. Use the EFI/UEFI option to boot your medium.
|
||||
* Disable Secure Boot—Even if you're using a distribution that officially supports Secure Boot, sometimes this doesn't work. In this case, the computer will most likely silently move on to the next boot loader, which could be your medium's BIOS-mode boot loader, resulting in a BIOS-mode boot. See [my page on Secure Boot][27] for some tips on how to disable Secure Boot.
|
||||
* If you can't seem to get the Linux installer to boot in EFI mode, try using a USB flash drive or CD-R version of my [rEFInd boot manager.][28] If rEFInd boots, it's guaranteed to be running in EFI mode, and on a UEFI-based PC, it will show only EFI-mode boot options, so if you can then boot to the Linux installer, it should be in EFI mode. (On Macs, though, rEFInd shows BIOS-mode boot options in addition to EFI-mode options.)
|
||||
|
||||
* **Preparing your ESP**—Except on Macs, EFIs use the ESP to hold boot loaders. If your computer came with Windows pre-installed, an ESP should already exist, and you can use it in Linux. If not, I recommend creating an ESP that's 550MiB in size. (If your existing ESP is smaller than this, go ahead and use it.) Create a FAT32 filesystem on it. If you use GParted or parted to prepare your disk, give the ESP a "boot flag." If you use GPT fdisk (gdisk, cgdisk, or sgdisk) to prepare the disk, give it a type code of EF00\. Some installers create a smallish ESP and put a FAT16 filesystem on it. This usually works fine, although if you subsequently need to re-install Windows, its installer will become confused by the FAT16 ESP, so you may need to back it up and convert it to FAT32 form.
|
||||
* **Using the ESP**—Different distributions' installers have different ways of identifying the ESP. For instance, some versions of Debian and Ubuntu call the ESP the "EFI boot partition" and do not show you an explicit mount point (although it will mount it behind the scenes); but a distribution like Arch or Gentoo will require you to mount it. The closest thing to a standard ESP mount point in Linux is /boot/efi, although /boot works well with some configurations—particularly if you want to use gummiboot or ELILO. Some distributions won't let you use a FAT partition as /boot, though. Thus, if you're asked to set a mount point for the ESP, make it /boot/efi. Do _not_ create a fresh filesystem on the ESP unless it doesn't already have one—if Windows or some other OS is already installed, its boot loader lives on the ESP, and creating a new filesystem will destroy that boot loader!
|
||||
* **Setting the boot loader location**—Some distributions may ask about the boot loader's (GRUB's) location. If you've properly flagged the ESP as such, this question should be unnecessary, but some distributions' installers still ask. Try telling it to use the ESP.
|
||||
* **Other partitions**—Other than the ESP, no other special partitions are required; you can set up root (/), swap, /home, or whatever else you like in the same way you would for a BIOS-mode installation. Note that you do _not_ need a [BIOS Boot Partition][39] for an EFI-mode installation, so if your installer is telling you that you need one, this may be a sign that you've accidentally booted in BIOS mode. On the other hand, if you create a BIOS Boot Partition, that will give you some extra flexibility, since you'll be able to install a BIOS version of GRUB to boot in either mode (EFI or BIOS).
|
||||
* **Fixing blank displays**—A problem that many people had through much of 2013 (but with decreasing frequency since then) was blank displays when booted in EFI mode. Sometimes this problem can be fixed by adding nomodeset to the kernel's command line. You can do this by typing e to open a simple text editor in GRUB. In many cases, though, you'll need to research this problem in more detail, because it often has more hardware-specific causes.
|
||||
|
||||
In some cases, you may be forced to install Linux in BIOS mode. You can sometimes then manually install an EFI-mode boot loader for Linux to begin booting in EFI mode. See my [Managing EFI Boot Loaders for Linux][53] page for information on available boot loaders and how to install them.
|
||||
|
||||
### Fixing Post-Installation Problems
|
||||
|
||||
If you can't seem to get an EFI-mode boot of Linux working but a BIOS-mode boot works, you can abandon EFI mode entirely. This is easiest on a Linux-only computer; just install a BIOS-mode boot loader (which the installer should have done if you installed in BIOS mode). If you're dual-booting with an EFI-mode Windows, though, the easiest solution is to install my [rEFInd boot manager.][54] Install it from Windows and edit the refind.conf file: Uncomment the scanfor line and ensure that hdbios is among the options. This will enable rEFInd to redirect the boot process to a BIOS-mode boot loader. This solution works for many systems, but sometimes it fails for one reason or another.
|
||||
|
||||
If you reboot the computer and it boots straight into Windows, it's likely that your Linux boot loader or boot manager was not properly installed. (You should try disabling Secure Boot first, though; as I've said, it often causes problems.) There are several possible solutions to this problem:
|
||||
|
||||
* **Use efibootmgr**—You can boot a Linux emergency disc _in EFI mode_ and use the efibootmgr utility to re-register your Linux boot loader, as described [here.][40]
|
||||
* **Use bcdedit in Windows**—In a Windows Administrator Command Prompt window, typing bcdedit /set {bootmgr} path \EFI\fedora\grubx64.efi will set the EFI/fedora/grubx64.efi file on the ESP as the default boot loader. Change that path as necessary to point to your desired boot loader. If you're booting with Secure Boot enabled, you should set shim.efi, shimx64.efi, or PreLoader.efi (whichever is present) as the boot program, rather than grubx64.efi.
|
||||
* **Install rEFInd**—Sometimes rEFInd can overcome this problem. I recommend testing by using the [CD-R or USB flash drive image.][41] If it can boot Linux, install the Debian package, RPM, or .zip file package. (Note that you may need to edit your boot options by highlighting a Linux vmlinuz* option and hitting F2 or Insert twice. This is most likely to be required if you've got a separate /bootpartition, since in this situation rEFInd can't locate the root (/) partition to pass to the kernel.)
|
||||
* **Use Boot Repair**—Ubuntu's [Boot Repair utility][42] can auto-repair some boot problems; however, I recommend using it only on Ubuntu and closely-related distributions, such as Mint. In some cases, it may be necessary to click the Advanced option and check the box to back up and replace the Windows boot loader.
|
||||
* **Hijack the Windows boot loader**—Some buggy EFIs boot only the Windows boot loader, which is called EFI/Microsoft/Boot/bootmgfw.efi on the ESP. Thus, you may need to rename this boot loader to something else (I recommend moving it down one level, to EFI/Microsoft/bootmgfw.efi) and putting a copy of your preferred boot loader in its place. (Most distributions put a copy of GRUB in a subdirectory of EFI named after themselves, such as EFI/ubuntu for Ubuntu or EFI/fedora for Fedora.) Note that this solution is an ugly hack, and some users have reported that Windows will replace its boot loader, so it may not even work 100% of the time. It is, however, the only solution that works on some badly broken EFIs. Before attempting this solution, I recommend upgrading your firmware and re-registering your own boot loader with efibootmgr in Linux or bcdedit in Windows.
|
||||
|
||||
Another class of problems relates to boot loader troubles—If you see GRUB (or whatever boot loader or boot manager your distribution uses by default) but it doesn't boot an OS, you must fix that problem. Windows often fails to boot because GRUB 2 is very finicky about booting Windows. This problem can be exacerbated by Secure Boot in some cases. See [my page on GRUB 2][55] for a sample GRUB 2 entry for booting Windows. Linux boot problems, once GRUB appears, can have a number of causes, and are likely to be similar to BIOS-mode Linux boot problems, so I don't cover them here.
|
||||
|
||||
Despite the fact that it's very common, my opinion of GRUB 2 is rather low—it's an immensely complex program that's difficult to configure and use. Thus, if you run into problems with GRUB, my initial response is to replace it with something else. [My Web page on EFI boot loaders for Linux][56] describes the options that are available. These include my own [rEFInd boot manager,][57] which is much easier to install and maintain, aside from the fact that many distributions do manage to get GRUB 2 working—but if you're considering replacing GRUB 2 because of its problems, that's obviously not worked out for you!
|
||||
|
||||
Beyond these issues, EFI booting problems can be quite idiosyncratic, so you may need to post to a Web forum for help. Be sure to describe the problem as thoroughly as you can. The [Boot Info Script][58] can provide useful information—run it and it should produce a file called RESULTS.txt that you can paste into your forum post. Be sure to precede this pasted text with the string [code] and follow it with [/code], though; otherwise people will complain. Alternatively, upload RESULTS.txt to a pastebin site, such as [pastebin.com,][59] and post the URL that the site gives you.
|
||||
|
||||
### Oops: Converting a Legacy-Mode Install to Boot in EFI Mode
|
||||
|
||||
**Warning:** These instructions are written primarily for UEFI-based PCs. If you've installed Linux in BIOS mode on a Mac but want to boot Linux in EFI mode, you can install your boot program _in OS X._ rEFInd (or the older rEFIt) is the usual choice on Macs, but GRUB can be made to work with some extra effort.
|
||||
|
||||
As of early 2015, one very common problem I see in online forums is that people follow bad instructions and install Linux in BIOS mode to dual-boot with an existing EFI-mode Windows installation. This configuration works poorly because most EFIs make it difficult to switch between boot modes, and GRUB can't handle the task, either. You might also find yourself in this situation if you've got a very flaky EFI that simply won't boot an external medium in EFI mode, or if you have video or other problems with Linux when it's booted in EFI mode.
|
||||
|
||||
As noted earlier, in [Fixing Post-Installation Problems,][60] one possible solution to such problems is to install rEFInd _in Windows_ and configure it to support BIOS-mode boots. You can then boot rEFInd and chainload to your BIOS-mode GRUB. I recommend this fix mainly when you have EFI-specific problems in Linux, such as a failure to use your video card. If you don't have such EFI-specific problems, installing rEFInd and a suitable EFI filesystem driver in Windows will enable you to boot Linux directly in EFI mode. This can be a perfectly good solution, and it will be equivalent to what I describe next.
|
||||
|
||||
In most cases, it's best to configure Linux to boot in EFI mode. There are many ways to do this, but the best way requires using an EFI-mode boot of Linux (or conceivably Windows or an EFI shell) to register an EFI-mode version of your preferred boot manager. One way to accomplish this goal is as follows:
|
||||
|
||||
1. Download a USB flash drive or CD-R version of my [rEFInd boot manager.][43]
|
||||
2. Prepare a medium from the image file you've downloaded. You can do this from any computer, booted in either EFI or BIOS mode (or in other ways on other platforms).
|
||||
3. If you've not already done so, [disable Secure Boot.][44] This is necessary because the rEFInd CD-R and USB images don't support Secure Boot. If you want to keep Secure Boot, you can re-enable it later.
|
||||
4. Boot rEFInd on your target computer. As described earlier, you may need to adjust your firmware settings and use the built-in boot manager to select your boot medium. The option you select may need to include the string _UEFI_ in its description.
|
||||
5. In rEFInd, examine the boot options. You should see at least one option for booting a Linux kernel (with a name that includes the string vmlinuz). Boot it in one of two ways:
|
||||
* If you do _not_ have a separate /boot partition, simply highlight the kernel and press Enter. Linux should boot.
|
||||
* If you _do_ have a separate /boot partition, press Insert or F2 twice. This action will open a line editor in which you can edit your kernel options. Add a root= specification to those options to identify your root (/) filesystem, as in root=/dev/sda5 if root (/) is on /dev/sda5. If you don't know what your root filesystem is, you should reboot in any way possible to figure it out.In some rare cases, you may need to add other kernel options instead of or in addition to a root= option. Gentoo with an LVM configuration requires dolvm, for example.
|
||||
6. Once Linux is booted, install your desired boot program. rEFInd is usually pretty easy to install via the RPM, Debian package, PPA, or binary .zip file referenced on the [rEFInd downloads page.][45] On Ubuntu and similar distributions, Boot Repair can fix your GRUB setup relatively simply, but it will be a bit of a leap of faith that it will work correctly. (It usually works fine, but in some cases it will make a hash of things.) Other options are described on my [Managing EFI Boot Loaders for Linux][46] page.
|
||||
7. If you want to boot with Secure Boot active, reboot and enable it. Note, however, that you may need to take extra installation steps to set up your boot program to use Secure Boot. Consult [my page on the topic][47] or your boot program's Secure Boot documentation for details.
|
||||
|
||||
When you reboot, you should see the boot program you just installed. If the computer instead boots into a BIOS-mode version of GRUB, you should enter your firmware and disable the BIOS/CSM/legacy support, or perhaps adjust your boot order options. If the computer boots straight to Windows, then you should read the preceding section, [Fixing Post-Installation Problems.][61]
|
||||
|
||||
You may want or need to tweak your configuration at this point. It's common to see extra boot options, or for an option you want to not be visible. Consult your boot program's documentation to learn how to make such changes.
|
||||
|
||||
### References and Additional Information
|
||||
|
||||
|
||||
* **Informational Web pages**
|
||||
* My [Managing EFI Boot Loaders for Linux][2] page covers the available EFI boot loaders and boot managers.
|
||||
* The [man page for OS X's bless tool][3] may be helpful in setting up a boot loader or boot manager on that platform.
|
||||
* [The EFI Boot Process][4] describes, in broad strokes, how EFI systems boot.
|
||||
* The [Arch Linux UEFI wiki page][5] has a great deal of information on UEFI and Linux.
|
||||
* Adam Williamson has written a good [summary of what EFI is and how it works.][6]
|
||||
* [This page][7] describes how to adjust EFI firmware settings from within Windows 8.
|
||||
* Matthew J. Garrett, the developer of the Shim boot loader to manage Secure Boot, maintains [a blog][8] in which he often writes about EFI issues.
|
||||
* If you're interested in developing EFI software yourself, my [Programming for EFI][9] can help you get started.
|
||||
* **Additional programs**
|
||||
* [The official rEFInd Web page][10]
|
||||
* [The official gummiboot Web page][11]
|
||||
* [The official ELILO Web page][12]
|
||||
* [The official GRUB Web page][13]
|
||||
* [The official GPT fdisk partitioning software Web page][14]
|
||||
* Ubuntu's [Boot Repair utility][15] can help fix some boot problems
|
||||
* **Communications**
|
||||
* The [rEFInd discussion forum on Sourceforge][16] provides a way to discuss rEFInd with other users or with me.
|
||||
* Pastebin sites, such as [http://pastebin.com,][17] provide a convenient way to exchange largeish text files with users on Web forums.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: http://www.rodsbooks.com/linux-uefi/
|
||||
|
||||
作者:[Roderick W. Smith][a]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:rodsmith@rodsbooks.com
|
||||
[1]:http://www.rodsbooks.com/efi-bootloaders/secureboot.html#disable
|
||||
[2]:http://www.rodsbooks.com/efi-bootloaders/
|
||||
[3]:http://ss64.com/osx/bless.html
|
||||
[4]:http://homepage.ntlworld.com/jonathan.deboynepollard/FGA/efi-boot-process.html
|
||||
[5]:https://wiki.archlinux.org/index.php/Unified_Extensible_Firmware_Interface
|
||||
[6]:https://www.happyassassin.net/2014/01/25/uefi-boot-how-does-that-actually-work-then/
|
||||
[7]:http://www.eightforums.com/tutorials/20256-uefi-firmware-settings-boot-inside-windows-8-a.html
|
||||
[8]:http://mjg59.dreamwidth.org/
|
||||
[9]:http://www.rodsbooks.com/efi-programming/
|
||||
[10]:http://www.rodsbooks.com/refind/
|
||||
[11]:http://freedesktop.org/wiki/Software/gummiboot
|
||||
[12]:http://elilo.sourceforge.net/
|
||||
[13]:http://www.gnu.org/software/grub/
|
||||
[14]:http://www.rodsbooks.com/gdisk/
|
||||
[15]:https://help.ubuntu.com/community/Boot-Repair
|
||||
[16]:https://sourceforge.net/p/refind/discussion/
|
||||
[17]:http://pastebin.com/
|
||||
[18]:http://www.rodsbooks.com/linux-uefi/#intro
|
||||
[19]:http://www.rodsbooks.com/linux-uefi/#isitefi
|
||||
[20]:http://www.rodsbooks.com/linux-uefi/#distributions
|
||||
[21]:http://www.rodsbooks.com/linux-uefi/#preparing
|
||||
[22]:http://www.rodsbooks.com/linux-uefi/#installing
|
||||
[23]:http://www.rodsbooks.com/linux-uefi/#troubleshooting
|
||||
[24]:http://www.rodsbooks.com/linux-uefi/#oops
|
||||
[25]:http://www.rodsbooks.com/linux-uefi/#references
|
||||
[26]:http://www.rodsbooks.com/linux-uefi/#using_firmware
|
||||
[27]:http://www.rodsbooks.com/efi-bootloaders/secureboot.html#disable
|
||||
[28]:http://www.rodsbooks.com/refind/getting.html
|
||||
[29]:https://en.wikipedia.org/wiki/Uefi
|
||||
[30]:https://en.wikipedia.org/wiki/BIOS
|
||||
[31]:http://www.rodsbooks.com/linux-uefi/#references
|
||||
[32]:http://www.eightforums.com/tutorials/20256-uefi-firmware-settings-boot-inside-windows-8-a.html
|
||||
[33]:http://www.eightforums.com/tutorials/6320-fast-startup-turn-off-windows-8-a.html
|
||||
[34]:http://www.rodsbooks.com/gdisk/
|
||||
[35]:http://en.wikipedia.org/wiki/EFI_System_partition
|
||||
[36]:http://www.rodsbooks.com/efi-bootloaders
|
||||
[37]:https://sourceforge.net/projects/windd/
|
||||
[38]:http://www.chrysocome.net/dd
|
||||
[39]:https://en.wikipedia.org/wiki/BIOS_Boot_partition
|
||||
[40]:http://www.rodsbooks.com/efi-bootloaders/installation.html
|
||||
[41]:http://www.rodsbooks.com/refind/getting.html
|
||||
[42]:https://help.ubuntu.com/community/Boot-Repair
|
||||
[43]:http://www.rodsbooks.com/refind/getting.html
|
||||
[44]:http://www.rodsbooks.com/efi-bootloaders/secureboot.html#disable
|
||||
[45]:http://www.rodsbooks.com/refind/getting.html
|
||||
[46]:http://www.rodsbooks.com/efi-bootloaders/
|
||||
[47]:http://www.rodsbooks.com/efi-bootloaders/secureboot.html
|
||||
[48]:mailto:rodsmith@rodsbooks.com
|
||||
[49]:http://ss64.com/osx/bless.html
|
||||
[50]:http://www.rodsbooks.com/refind/getting.html
|
||||
[51]:http://www.rodsbooks.com/linux-uefi/#oops
|
||||
[52]:http://www.rodsbooks.com/linux-uefi/#oops
|
||||
[53]:http://www.rodsbooks.com/efi-bootloaders/
|
||||
[54]:http://www.rodsbooks.com/refind/
|
||||
[55]:http://www.rodsbooks.com/efi-bootloaders/grub2.html
|
||||
[56]:http://www.rodsbooks.com/efi-bootloaders
|
||||
[57]:http://www.rodsbooks.com/refind/
|
||||
[58]:http://sourceforge.net/projects/bootinfoscript/
|
||||
[59]:http://pastebin.com/
|
||||
[60]:http://www.rodsbooks.com/linux-uefi/#troubleshooting
|
||||
[61]:http://www.rodsbooks.com/linux-uefi/#troubleshooting
|
@ -1,119 +0,0 @@
|
||||
[kenxx](https://github.com/kenxx)
|
||||
|
||||
[Why (most) High Level Languages are Slow][7]
|
||||
============================================================
|
||||
|
||||
Contents
|
||||
|
||||
|
||||
* * [Cache costs review][1]
|
||||
* [Why C# introduces cache misses][2]
|
||||
* [Garbage Collection][3]
|
||||
* [Closing remarks][5]
|
||||
|
||||
|
||||
In the last month or two I’ve had basically the same conversation half a dozen times, both online and in real life, so I figured I’d just write up a blog post that I can refer to in the future.
|
||||
|
||||
The reason most high level languages are slow is usually because of two reasons:
|
||||
|
||||
1. They don’t play well with the cache.
|
||||
2. They have to do expensive garbage collections
|
||||
|
||||
But really, both of these boil down to a single reason: the language heavily encourages too many allocations.
|
||||
|
||||
First, I’ll just state up front that for all of this I’m talking mostly about client-side applications. If you’re spending 99.9% of your time waiting on the network then it probably doesn’t matter how slow your language is – optimizing network is your main concern. I’m talking about applications where local execution speed is important.
|
||||
|
||||
I’m going to pick on C# as the specific example here for two reasons: the first is that it’s the high level language I use most often these days, and because if I used Java I’d get a bunch of C# fans telling me how it has value types and therefore doesn’t have these issues (this is wrong).
|
||||
|
||||
In the following I will be talking about what happens when you write idiomatic code. When you work “with the grain” of the language. When you write code in the style of the standard libraries and tutorials. I’m not very interested in ugly workarounds as “proof” that there’s no problem. Yes, you can sometimes fight the language to avoid a particular issue, but that doesn’t make the language unproblematic.
|
||||
|
||||
### Cache costs review
|
||||
|
||||
First, let’s review the importance of playing well with the cache. Here’s a graph based on [this data][10] on memory latencies for Haswell:
|
||||
|
||||
![](https://www.sebastiansylvan.com/img/041315_0638_whymosthigh1.png)
|
||||
|
||||
The latency for this particular CPU to get to memory is about 230 cycles, meanwhile the cost of reading data from L1 is 4 cycles. The key takeaway here is that doing the wrong thing for the cache can make code ~50x slower. In fact, it may be even worse than that – modern CPUs can often do multiple things at once so you could be loading stuff from L1 while operating on stuff that’s already in registers, thus hiding the L1 load cost partially or completely.
|
||||
|
||||
Without exaggerating we can say that aside from making reasonable algorithm choices, cache misses are the main thing you need to worry about for performance. Once you’re accessing data efficiently you can worry about fine tuning the actual operations you do. In comparison to cache misses, minor inefficiencies just don’t matter much.
|
||||
|
||||
This is actually good news for language designers! You don’t _have_ to build the most efficient compiler on the planet, and you totally can get away with some extra overhead here and there for your abstractions (e.g. array bounds checking), all you need to do is make sure that your design makes it easy to write code that accesses data efficiently and programs in your language won’t have any problems running at speeds that are competitive with C.
|
||||
|
||||
### Why C# introduces cache misses
|
||||
|
||||
To put it bluntly, C# is a language that simply isn’t designed to run efficiently with modern cache realities in mind. Again, I’m now talking about the limitations of the design and the “pressure” it puts on the programmer to do things in inefficient ways. Many of these things have theoretical workarounds that you could do at great inconvenience. I’m talking about idiomatic code, what the language “wants” you to do.
|
||||
|
||||
The basic problem with C# is that it has very poor support for value-based programming. Yes, it has structs which are values that are stored “embedded” where they are declared (e.g. on the stack, or inside another object). But there are a several big issues with structs that make them more of a band-aid than a solution.
|
||||
|
||||
* You have to declare your data types as struct up front – which means that if you _ever_ need this type to exist as a heap allocation then _all_ of them need to be heap allocations. You could make some kind of class-wrapper for your struct and forward all the members but it’s pretty painful. It would be better if classes and structs were declared the same way and could be used in both ways on a case-by-case basis. So when something can live on the stack you declare it as a value, and when it needs to be on the heap you declare it as an object. This is how C++ works, for example. You’re not encouraged to make everything into an object-type just because there’s a few things here and there that need them on the heap.
|
||||
|
||||
* _Referencing_ values is extremely limited. You can pass values by reference to functions, but that’s about it. You can’t just grab a reference to an element in a List<int>, you have to store both a reference to the list and an index. You can’t grab a pointer to a stack-allocated value, or a value stored inside an object (or value). You can only copy them, unless you’re passing them to a function (by ref). This is all understandable, by the way. If type safety is a priority, it’s pretty difficult (though not imposible) to support flexible referencing of values while also guaranteeing type safety. The rationale behind these restrictions don’t change the fact that the restrictions are there, though.</int>
|
||||
|
||||
* [Fixed sized buffers][6] don’t support custom types and also requires you to use an unsafe keyword.
|
||||
|
||||
* Limited “array slice” functionality. There’s an ArraySegment class, but it’s not really used by anyone, which means that in order to pass a range of elements from an array you have to create an IEnumerable, which means allocation (boxing). Even if the APIs accepted ArraySegment parameters it’s still not good enough – you can only use it for normal arrays, not for List<t>, not for [stack-allocated array][4]s, etc.</t>
|
||||
|
||||
The bottom line is that for all but very simple cases, the language pushes you very strongly towards heap allocations. If all your data is on the heap, it means that accessing it is likely to cause a cache misses (since you can’t decide how objects are organized in the heap). So while a C++ program poses few challenges to ensuring that data is organized in cache-efficient ways, C# typically encourages you to allocate each part of that data in a separate heap allocation. This means the programmers loses control over data layout, which means unnecessary cache misses are introduced and performance drops precipitously. It doesn’t matter that [you can now compile C# programs natively][11] ahead of time – improvement to code quality is a drop in the bucket compared to poor memory locality.
|
||||
|
||||
Plus, there’s storage overhead. Each reference is 8 bytes on a 64-bit machine, and each allocation has its own overhead in the form of various metadata. A heap full of tiny objects with lots of references everywhere has a lot of space overhead compared to a heap with very few large allocations where most data is just stored embedded within their owners at fixed offsets. Even if you don’t care about memory requirements, the fact that the heap is bloated with header words and references means that cache lines have more waste in them, this in turn means even more cache misses and reduced performance.
|
||||
|
||||
There are sometimes workarounds you can do, for example you can use structs and allocate them in a pool using a big List<t>. This allows you to e.g. traverse the pool and update all of the objects in-bulk, getting good locality. This does get pretty messy though, because now anything else wanting to refer to one of these objects have to have a reference to the pool as well as an index, and then keep doing array-indexing all over the place. For this reason, and the reasons above, it is significantly more painful to do this sort of stuff in C# than it is to do it in C++, because it’s just not something the language was designed to do. Furthermore, accessing a single element in the pool is now more expensive than just having an allocation per object - you now get _two_ cache misses because you have to first dereference the pool itself (since it’s a class). Ok, so you can duplicate the functionality of List<t> in struct-form and avoid this extra cache miss and make things even uglier. I’ve written plenty of code just like this and it’s just extremely low level and error prone.</t></t>
|
||||
|
||||
Finally, I want to point out that this isn’t just an issue for “hot spot” code. Idiomatically written C# code tends to have classes and references basically _everywhere_ . This means that all over your code at relatively uniform frequency there are random multi-hundred cycle stalls, dwarfing the cost of surrounding operations. Yes there could be hotspots too, but after you’ve optimized them you’re left with a program that’s just [uniformly slow.][12] So unless you want to write all your code with memory pools and indices, effectively operating at a lower level of abstraction than even C++ does (and at that point, why bother with C#?), there’s not a ton you can do to avoid this issue.
|
||||
|
||||
### Garbage Collection
|
||||
|
||||
I’m just going to assume in the following that you already understand why garbage collection is a performance problem in a lot of cases. That pausing randomly for many milliseconds just is usually unacceptable for anything with animation. I won’t linger on it and move on to explaining why the language design itself exacerbates this issue.
|
||||
|
||||
Because of the limitations when it comes to dealing with values, the language very strongly discourages you from using big chunky allocations consisting mostly of values embedded within other values (perhaps stored on the stack), pressuring you instead to use lots of small classes which have to be allocated on the heap. Roughly speaking, more allocations means more time spent collecting garbage.
|
||||
|
||||
There are benchmarks that show how C# or Java beat C++ in some particular case, because an allocator based on a GC can have decent throughput (cheap allocations, and you batch all the deallocations up). However, this isn’t a common real world scenario. It takes a huge amount of effort to write a C# program with the same low allocation rate that even a very naïve C++ program has, so those kinds of comparisons are really comparing a highly tuned managed program with a naïve native one. Once you spend the same amount of effort on the C++ program, you’d be miles ahead of C# again.
|
||||
|
||||
I’m relatively convinced that you could write a GC more suitable for high performance and low latency applications (e.g. an incremental GC where you spend a fixed amount of time per frame doing collection), but this is not enough on its own. At the end of the day the biggest issue with most high level languages is simply that the design encourages far too much garbage being created in the first place. If idiomatic C# allocated at the same low rate a C program does, the GC would pose far fewer problems for high performance applications. And if you _did_ have an incremental GC to support soft real-time applications, you’ll probably need a write barrier for it – which, as cheap as it is, means that a language that encourages pointers will add a performance tax to the mutators.
|
||||
|
||||
Look at the base class library for .Net, allocations are everywhere! By my count the [.Net Core Framework][13] contains 19x more public classes than structs, so in order to use it you’re very much expected to do quite a lot of allocation. Even the creators of .Net couldn’t resist the siren call of the language design! I don’t know how to gather statistics on this, but using the base class library you quickly notice that it’s not just in their choice of value vs. object types where the allocation-happiness shines through. Even _within_ this code there’s just a ton of allocations. Everything seems to be written with the assumption that allocations are cheap. Hell, you can’t even print an int without allocating! Let that sink in for a second. Even with a pre-sized StringBuilder you can’t stick an int in there without allocating using the standard library. That’s pretty silly if you ask me.
|
||||
|
||||
This isn’t just in the standard library. Other C# libraries follow suit. Even Unity (a _game engine_ , presumably caring more than average about performance issues) has APIs all over the place that return allocated objects (or arrays) or force the caller to allocate to call them. For example, by returning an array from GetComponents, they’re forcing an array allocation just to see what components are on a GameObject. There are a number of alternative APIs they could’ve chosen, but going with the grain of the language means allocations. The Unity folks wrote “Good C#”, it’s just bad for performance.
|
||||
|
||||
# Closing remarks
|
||||
|
||||
If you’re designing a new language, _please_ consider efficiency up front. It’s not something a “Sufficiently Smart Compiler” can fix after you’ve already made it impossible. Yes, it’s hard to do type safety without a garbage collector. Yes, it’s harder to do garbage collection when you don’t have uniform representation for data. Yes, it’s hard to reason about scoping rules when you can have pointers to random values. Yes, there are tons of problems to figure out here, but isn’t figuring those problems out what language design is supposed to be? Why make another minor iteration of languages that were already designed in the 1960s?
|
||||
|
||||
Even if you can’t fix all these issues, maybe you can get most of the way there? Maybe use region types (a la Rust) to ensure safety. Or maybe even consider abandoning “type safety at all costs” in favor of more runtime checks (if they don’t cause extra cache misses, they don’t really matter… and in fact C# already does similar things, see covariant arrays which are strictly speaking a type system violation, and leads to a runtime exception).
|
||||
|
||||
The bottom line is that if you want to be an alternative to C++ for high performance scenarios, you need to worry about data layout and locality.
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
作者简介:
|
||||
|
||||
My name is Sebastian Sylvan. I’m from Sweden but live in Seattle. I work at Microsoft on Hololens. Obviously my views are my own and don’t necessarily represent those of Microsoft.
|
||||
|
||||
I typically blog graphics, languages, performance, and such. Feel free to hit me up on twitter or email (see links in sidebar).
|
||||
|
||||
------------
|
||||
|
||||
|
||||
via: https://www.sebastiansylvan.com/post/why-most-high-level-languages-are-slow
|
||||
|
||||
作者:[Sebastian Sylvan ][a]
|
||||
译者:[kenxx](https://github.com/kenxx)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:https://www.sebastiansylvan.com/about/
|
||||
[1]:https://www.sebastiansylvan.com/post/why-most-high-level-languages-are-slow/?imm_mid=0ee8ca&cmp=em-prog-na-na-newsltr_20170311#cache-costs-review
|
||||
[2]:https://www.sebastiansylvan.com/post/why-most-high-level-languages-are-slow/?imm_mid=0ee8ca&cmp=em-prog-na-na-newsltr_20170311#why-c-introduces-cache-misses
|
||||
[3]:https://www.sebastiansylvan.com/post/why-most-high-level-languages-are-slow/?imm_mid=0ee8ca&cmp=em-prog-na-na-newsltr_20170311#garbage-collection
|
||||
[4]:https://msdn.microsoft.com/en-us/library/vstudio/cx9s2sy4(v=vs.100).aspx
|
||||
[5]:https://www.sebastiansylvan.com/post/why-most-high-level-languages-are-slow/?imm_mid=0ee8ca&cmp=em-prog-na-na-newsltr_20170311#closing-remarks
|
||||
[6]:https://msdn.microsoft.com/en-us/library/vstudio/zycewsya(v=vs.100).aspx
|
||||
[7]:https://www.sebastiansylvan.com/post/why-most-high-level-languages-are-slow/
|
||||
[8]:https://www.sebastiansylvan.com/categories/programming-languages
|
||||
[9]:https://www.sebastiansylvan.com/categories/software-engineering
|
||||
[10]:http://www.7-cpu.com/cpu/Haswell.html
|
||||
[11]:https://msdn.microsoft.com/en-us/vstudio/dotnetnative.aspx
|
||||
[12]:http://c2.com/cgi/wiki?UniformlySlowCode
|
||||
[13]:https://github.com/dotnet/corefx
|
@ -0,0 +1,87 @@
|
||||
Useful Vim editor plugins for software developers - part 3: a.vim
|
||||
============================================================
|
||||
|
||||
Up until now, in this series of articles covering programming-related plugins for Vim, we discussed basics of Vim plugin installation using Pathogen plugin manager, and covered a total of three plugins - namely [Tagbar, delimitMate][2], and [Syntastic][3]. Now, in this third and the final part, we will discuss another very useful plugin dubbed **a.vim**.
|
||||
|
||||
Please note that all the examples, commands, and instructions mentioned in this tutorial have been tested on Ubuntu 16.04, and the Vim version we've used is 7.4.
|
||||
|
||||
### A.vim
|
||||
|
||||
If you are into software development and work with programming languages like C and C++, you'd agree that it's practically impossible to tell how many times you have to switch between source and header files. What I mean to say is that it's a very basic task that's done very frequently.
|
||||
|
||||
While it's easy to switch between files when using a GUI-based IDE (as you just have to do a couple of mouse clicks), things get a bit difficult if you are using a command line IDE like Vim. However, as you'd have guessed by now, there exists a plugin - called a.vim - that makes life easier for you especially when it comes to switching between source and header files.
|
||||
|
||||
Before we jump over to the usage, it's worth mentioning that installation process for this plugin is different from the way we've be installing the other plugins discussed in this article series. Following are the steps involved in this case:
|
||||
|
||||
* Firstly, you need to download a couple of files (a.vim and alternate.txt), which you can do by heading [here][1].
|
||||
* Next, create the following directories: ~/.`vim/bundle/avim`, ~/.`vim/bundle/avim/doc`, ~/.`vim/bundle/avim/plugin`, and ~/.`vim/bundle/autoload.`
|
||||
* Once the directory creation is done, put a.vim into ~/.vim/bundle/avim/plugin as well as ~/.vim/bundle/autoload, and alternate.txt into ~/.vim/bundle/avim/doc.
|
||||
|
||||
That's it. If all the aforementioned steps are completed successfully, the plugin will be installed on your system.
|
||||
|
||||
Using the plugin is very simple: All you have to do is to run the **:A** command - if the current file is a source file (say test.c), the plugin will open the corresponding header file (test.h) for you (vice-versa is true as well).
|
||||
|
||||
Obviously, not every time a corresponding file with the same name exists. In these cases, running **:A** will create a new file with that name. For example, if test.h doesn't exist, a file with that name will be created and opened for you.
|
||||
|
||||
In case you want to disable this behavior, you can set the g:alternateNoDefaultAlternate variable (give it a non-zero value) in the .vimrc file present in your home directory.
|
||||
|
||||
Moving on, it's pretty normal to refer to a non-corresponding header file from a source file as well. For example, if you are in test.c, and want to switch to some other header file (say mem.h) and not test.h, then you can do that by running the :**IH <filename>** command in Vim. Needless to say, you'll have to replace <filename> with the actual name of the file you need to open.
|
||||
|
||||
Up until now, whatever functionality we discussed is assuming that the file that you intend to open is present in the same directory as the current file. However, as you'd agree, it's not always the case. What I mean to say is that in many projects the location of the source files and the corresponding header files is not always the same directory.
|
||||
|
||||
To handle such situations, you need to use the g:alternateSearchPath variable. This is what the official documentation [says][4] about this variable:
|
||||
|
||||
"This plugin allows the search path it uses to locate source and header files to be configured. The search path is specified by setting the g:alternateSearchPath variable. The default setting is as follows:"
|
||||
|
||||
```
|
||||
g:alternateSearchPath = 'sfr:../source,sfr:../src,sfr:../include,sfr:../inc'
|
||||
```
|
||||
|
||||
"This indicates that the corresponding file will be searched for in ../source, ../src. ../include and ../inc all relative to the current file being switched from. The value of the g:alternateSearchPath variable is simply a comma separated list of prefixes and directories. The "sfr:" prefix indicates that the path is relative to the file. Other prefixes are "wdr:" which indicates that the directory is relative to the current working directory and "abs:" which indicates the path is absolute. If no prefix is specified "sfr:" is assumed."
|
||||
|
||||
If all the aforementioned features of this plugin have already made you go "wow," let me tell you that's not all. Another extremely useful functionality the plugin offers is the ability to split your Vim screen so that both source and its corresponding header file can be viewed simultaneously.
|
||||
|
||||
What more, you can split the screen both horizontally and vertically, depending upon what fits best for you. Use **:AS** command to split the screen horizontally and **:AV** command for vertical split.
|
||||
|
||||
[
|
||||
![Vim.a vertical split screen](https://www.howtoforge.com/images/vim-editor-plugins-for-software-developers-3/vim-ver-split.png)
|
||||
][5]
|
||||
|
||||
[
|
||||
![vim.a horizontal split screen](https://www.howtoforge.com/images/vim-editor-plugins-for-software-developers-3/vim-hor-split.png)
|
||||
][6]
|
||||
|
||||
Use **:A** command to switch between opened files.
|
||||
|
||||
The plugin also lets you open a corresponding file in a separate tab within the same Vim window. You can do this by running the **:AT** command.
|
||||
|
||||
[
|
||||
![tabs in Vim with a.vim.](https://www.howtoforge.com/images/vim-editor-plugins-for-software-developers-3/vim-tab1.png)
|
||||
][7]
|
||||
|
||||
Of course, like **:AV**, **:AS**, and **:AT**, you can use **:IHV**, **:IHS**, and **:IHT** commands as well.
|
||||
|
||||
### Conclusion
|
||||
|
||||
While there are many programming-related Vim plugins available to use, the ones that we discussed in this three-part series should be enough to give you an idea about how powerful the editor becomes if you have the correct plugins enabled for your software development work.
|
||||
|
||||
**Of course, we've only focused on the programming part here. For those of you who use Vim as you daily text editor, you should know that there are a plethora of plugins available that add to Vim's text editing functionality and make it even better. But we'll leave this discussion for some other day.**
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.howtoforge.com/tutorial/vim-editor-plugins-for-software-developers-3/
|
||||
|
||||
作者:[Ansh][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/vim-editor-plugins-for-software-developers-3/
|
||||
[1]:http://www.vim.org/scripts/script.php?script_id=31
|
||||
[2]:https://www.howtoforge.com/tutorial/vim-editor-plugins-for-software-developers/
|
||||
[3]:https://www.howtoforge.com/tutorial/vim-editor-plugins-for-software-developers-2-syntastic/
|
||||
[4]:https://github.com/csliu/a.vim/blob/master/doc/alternate.txt
|
||||
[5]:https://www.howtoforge.com/images/vim-editor-plugins-for-software-developers-3/big/vim-ver-split.png
|
||||
[6]:https://www.howtoforge.com/images/vim-editor-plugins-for-software-developers-3/big/vim-hor-split.png
|
||||
[7]:https://www.howtoforge.com/images/vim-editor-plugins-for-software-developers-3/big/vim-tab1.png
|
@ -1,85 +0,0 @@
|
||||
Yuan0302 Translating
|
||||
FTPS (FTP over SSL) vs SFTP (SSH File Transfer Protocol)
|
||||
============================================================
|
||||
|
||||
[
|
||||
![ftps sftp](http://www.techmixer.com/pic/2015/07/ftps-sftp.png "ftps sftp")
|
||||
][5]
|
||||
|
||||
![ftps sftp](data:image/gif;base64,R0lGODdhAQABAPAAAP///wAAACwAAAAAAQABAEACAkQBADs= "ftps sftp")
|
||||
|
||||
**SSH File transfer protocol, SFTP** or **File Transfer protocol via Secure Socket Layer, **FTPS are the most common secure FTP communication technologies used to transfer computer files from one host to another host over a TCP networks. Both SFTP and FTPS offer a high level file transfer security protection with strong algorithms such as AES and Triple DES to encrypt any data transferred.
|
||||
|
||||
But the most notable differences between SFTP and FTPS is how connections are authenticated and managed.
|
||||
|
||||
FTPS is FTP utilising Secure Secure Layer (SSL) certificate for Security. The entire secure FTP connection is authenticated using an User ID, Password and SSL certificate. Once FTPS connection established, [FTP client software][6] will check destination [FTP server ][7]if the server’s certificate is trusted.
|
||||
|
||||
The SSL certificate will considered trusted if either the certificate was signed off by a known certificate authority (CA) or if the certificate was self-signed (by your partner) and you have a copy of their public certificate in your trusted key store. All username and password information for FTPS will be encrypted through secure FTP connection.
|
||||
|
||||
### Below are the FTPS pros and cons:
|
||||
|
||||
Pros:
|
||||
|
||||
* The communication can be read and understood by a human
|
||||
* Provides services for server-to-server file transfer
|
||||
* SSL/TLS has good authentication mechanisms (X.509 certificate features)
|
||||
* FTP and SSL support is built into many internet communications frameworks
|
||||
|
||||
Cons:
|
||||
|
||||
* Does not have a uniform directory listing format
|
||||
* Requires a secondary DATA channel, which makes it hard to use behind firewalls
|
||||
* Does not define a standard for file name character sets (encodings)
|
||||
* Not all FTP servers support SSL/TLS
|
||||
* Does not have a standard way to get and change file or directory attributes
|
||||
|
||||
SFTP or SSH File Transfer Protocol is another secure Secure File Transfer Protocol is designed as a SSH extension to provide file transfer capability, so it usually uses only the SSH port for both data and control. When your [FTP client][8] software connect to SFTP server, it will transmit public key to the server for authentication. If the keys match, along with any user/password supplied, then the authentication will succeed.
|
||||
|
||||
### Below are the SFTP Pros and Cons:
|
||||
|
||||
Pros:
|
||||
|
||||
* Has only one connection (no need for a DATA connection).
|
||||
* FTP connection is always secured
|
||||
* FTP directory listing is uniform and machine-readable
|
||||
* FTP protocol includes operations for permission and attribute manipulation, file locking, and more functionality.
|
||||
|
||||
Cons:
|
||||
|
||||
* The communication is binary and can not be logged “as is” for human reading
|
||||
SSH keys are harder to manage and validate.
|
||||
* The standards define certain things as optional or recommended, which leads to certain compatibility problems between different software titles from different vendors.
|
||||
* No server-to-server copy and recursive directory removal operations
|
||||
* No built-in SSH/SFTP support in VCL and .NET frameworks.
|
||||
|
||||
Overall most of FTP server software support both secure FTP technologies with strong authentication options.
|
||||
|
||||
But SFTP will be clear winner since it’s very firewall friendly. SFTP only needs a single port number (default of 22) to be opened through the firewall. This port will be used for all SFTP communications, including the initial authentication, any commands issued, as well as any data transferred.
|
||||
|
||||
FTPS will be more difficult to implement through a tightly secure firewall since FTPS uses multiple network port numbers. Every time a file transfer request (get, put) or directory listing request is made, another port number needs to be opened. Therefore it have to open a range of ports in your firewalls to allow for FTPS connections, which can be a security risk for your network.
|
||||
|
||||
FTP Server software that supports FTPS and SFTP:
|
||||
|
||||
1. [Cerberus FTP Server][2]
|
||||
2. [FileZilla – Most famous free FTPs and FTPS server software][3]
|
||||
3. [Serv-U FTP Server][4]
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: http://www.techmixer.com/ftps-sftp/
|
||||
|
||||
作者:[Techmixer.com][a]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:http://www.techmixer.com/
|
||||
[1]:http://www.techmixer.com/ftps-sftp/#respond
|
||||
[2]:http://www.cerberusftp.com/
|
||||
[3]:http://www.techmixer.com/free-ftp-server-best-windows-ftp-server-download/
|
||||
[4]:http://www.serv-u.com/
|
||||
[5]:http://www.techmixer.com/pic/2015/07/ftps-sftp.png
|
||||
[6]:http://www.techmixer.com/free-ftp-file-transfer-protocol-softwares/
|
||||
[7]:http://www.techmixer.com/free-ftp-server-best-windows-ftp-server-download/
|
||||
[8]:http://www.techmixer.com/best-free-mac-ftp-client-connect-ftp-server/
|
@ -1,72 +0,0 @@
|
||||
...being translated by mec2cod...
|
||||
|
||||
How to Keep Hackers out of Your Linux Machine Part 2: Three More Easy Security Tips
|
||||
============================================================
|
||||
|
||||
|
||||
![security tips](https://www.linux.com/sites/lcom/files/styles/rendered_file/public/security-tips.jpg?itok=JMp34oc3 "security tips")
|
||||
In this series, we’ll cover essential information for keeping hackers out of your system. Watch the free webinar on-demand for more information.[Creative Commons Zero][1]Pixabay
|
||||
|
||||
In [part 1][3] of this series, I shared two easy ways to prevent hackers from eating your Linux machine. Here are three more tips from my recent Linux Foundation webinar where I shared more tactics, tools and methods hackers use to invade your space. Watch the entire [webinar on-demand][4] for free.
|
||||
|
||||
### Easy Linux Security Tip #3
|
||||
|
||||
**Sudo.**
|
||||
|
||||
Sudo is really, really important. I realize this is just really basic stuff but these basic things make my life as a hacker so much more difficult. If you don't have it configured, configure it.
|
||||
|
||||
Also, all your users must use their password. Don't all “sudo all” with no password. That doesn't do anything other than make my life easy when I have a user that has “sudo all” with no password. If I can “sudo <blah>” and hit you without having to authenticate again and I have your SSH key with no passphrase, that makes it pretty easy to get around. I now have root on your machine.
|
||||
|
||||
Keep the timeout low. We like to hijack sessions, and if you have a user that has Sudo and the timeout is three hours and I hijack your session, then you've given me a free pass again even though you require a password.
|
||||
|
||||
I recommend a timeout of about 10 minutes, or even 5 minutes. They’ll enter their password over and over again but if you keep the timeout low, then you reduce your attack surface.
|
||||
|
||||
Also limit the available commands and don't allow shell access with sudo. Most default distributions right now will allow you to do “sudo bash” and get a root shell, which is great if you are doing massive amounts of admin tasks. However, most users should have a limited amount of commands that they need to actually run. The more you limit them, the smaller your attack surface. If you give me shell access I am going to be able to do all kinds of stuff.
|
||||
|
||||
### Easy Linux Security Tip #4
|
||||
|
||||
**Limit running services.**
|
||||
|
||||
Firewalls are great. Your perimeter firewall is awesome. There are several manufacturers out there that do a fantastic job when the traffic comes across your network. But what about the people on the inside?
|
||||
|
||||
Are you using a host-based firewall or host-based intrusion detection system? If so, configure it right. How do you know if something goes wrong that you are still protected?
|
||||
|
||||
The answer is to limit the services that are currently running. Don't run mySQL on a machine that doesn't need it. If you have a distribution that installs a full LAMP stack by default and you're not running anything on top of it, then uninstall it. Disable those services and don't start them.
|
||||
|
||||
And make sure users don't have default credentials. Make sure that those contents are configured securely. If you are running Tomcat, you are not allowed to upload your own applets. Make sure they don't run as root. If I am able to run an applet, I don't want to be able to run an applet as root and give myself access. The more you can restrict the amount of things that people can do the better off it is going to be.
|
||||
|
||||
### Easy Linux Security Tip #5
|
||||
|
||||
**Watch your logs.**
|
||||
|
||||
Look at them. Seriously. Watch your logs. We ran into an issue six months ago where one of our customers wasn't looking at their logs and they have been owned for a very, very long time. Had they been watching it, they would have been able to tell that their machines have been compromised and their whole network was wide open. I do this at home. I have a regimen every morning. I get up, I check my email. I go through my logs, and it takes me 15 minutes but it tells me a wealth of information about what's going on.
|
||||
|
||||
Just this morning, I had three systems fail in the cabinet and I had to go and reboot them, and I have no idea why but I could tell in my logs that they weren't responding. They were lab systems. I really don't care about them but other people do.
|
||||
|
||||
Centralizing your logging via Syslog or Splunk or any of those logging consolidation tools is fantastic. It is better than keeping them local. My favorite thing to do is to edit your logs so you don't know that I have been there. If I can do that then you have no clue. It's much more difficult for me to modify a central set of logs than a local set.
|
||||
|
||||
Just like your significant other, bring them flowers, aka, disk space. Make sure you have plenty of disk space available for logging. Going into a read-only file system is not a good thing.
|
||||
|
||||
Also, know what's abnormal. It’s such a difficult thing to do but in the long run it is going to pay dividends. You’ll know what's going on and when something’s wrong. Be sure you know that.
|
||||
|
||||
In the [third and final blog post][5], I’ll answer some of the excellent security questions asked during the webinar. [Watch the entire free webinar on-demand][6] now.
|
||||
|
||||
_Mike Guthrie works for the Department of Energy doing Red Team engagements and penetration testing._
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.linux.com/news/webinar/2017/how-keep-hackers-out-your-linux-machine-part-2-three-more-easy-security-tips
|
||||
|
||||
作者:[MIKE GUTHRIE][a]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:https://www.linux.com/users/anch
|
||||
[1]:https://www.linux.com/licenses/category/creative-commons-zero
|
||||
[2]:https://www.linux.com/files/images/security-tipsjpg
|
||||
[3]:https://www.linux.com/news/webinar/2017/how-keep-hackers-out-your-linux-machine-part-1-top-two-security-tips
|
||||
[4]:http://portal.on24.com/view/channel/index.html?showId=1101876&showCode=linux&partnerref=linco
|
||||
[5]:https://www.linux.com/news/webinar/2017/how-keep-hackers-out-your-linux-machine-part-3-your-questions-answered
|
||||
[6]:http://bit.ly/2j89ISJ
|
@ -1,127 +0,0 @@
|
||||
yangmingming translating
|
||||
How to access shell or run external commands from within Vim
|
||||
============================================================
|
||||
|
||||
### On this page
|
||||
|
||||
1. [Execute external commands in Vim][1]
|
||||
2. [Access Shell in Vim][2]
|
||||
3. [The loophole to keep in mind][3]
|
||||
|
||||
Vim, as you might already know, is a feature-packed and powerful editor. Here at HowtoForge, we've written several tutorials on Vim, covering its [basic usage][4], [plugins][5], as well as some [other][6] [useful][7] features. But given the sea of features Vim offers, we always find something useful to share with our readership.
|
||||
|
||||
In this tutorial, we will focus on how you can execute external commands as well as access the command line shell from within the editor window.
|
||||
|
||||
But before we start doing that, it's worth mentioning that all the examples, commands, and instructions mentioned in this tutorial have been tested on Ubuntu 14.04, and the Vim version we've used is 7.4.
|
||||
|
||||
### Execute external commands in Vim
|
||||
|
||||
Sometimes you might want to execute external commands from within the Vim editor window. For example, consider a situation where-in you've opened a file in Vim, made some changes, and then while trying to save those changes, Vim throws an error saying you don't have sufficient permissions.
|
||||
|
||||
[
|
||||
![Execute commands in VIM](https://www.howtoforge.com/images/how-to-access-shell-or-run-external-commands-from-within-vim/vim-perm-error.png)
|
||||
][8]
|
||||
|
||||
Now, exiting the current vim session and again opening the file with sufficient privileges will mean loss of all the changes you've done, so that'd, you'll agree, not be an option in most of the cases. It's situations like these where the ability to run external commands from within the editor comes in handy.
|
||||
|
||||
We'll come back to the above use-case later(**), but for now, let's understand how you can run basic commands from within vim.
|
||||
|
||||
Suppose while editing a file, you want to know the number of lines, words, and characters the file contains. To do this, in the command mode of Vim, just input colon (:) followed by a bang (!) and finally the command ('wc' in this case) followed by the file name (use % for current file).
|
||||
|
||||
```
|
||||
:! wc %
|
||||
```
|
||||
|
||||
Here's an example:
|
||||
|
||||
File with the aforementioned command ready to be executed:
|
||||
|
||||
[
|
||||
![Command ready to be executed in VIM](https://www.howtoforge.com/images/how-to-access-shell-or-run-external-commands-from-within-vim/vim-count-lines.png)
|
||||
][9]
|
||||
|
||||
and here's the output on the terminal:
|
||||
|
||||
[
|
||||
![command output](https://www.howtoforge.com/images/how-to-access-shell-or-run-external-commands-from-within-vim/vim-wc-output.png)
|
||||
][10]
|
||||
|
||||
After you are done seeing the output, press the Enter key and you'll be taken back to your Vim session.
|
||||
|
||||
This feature can come in really handy in situations where, say, you are writing a code or script, and want to quickly know whether or not the code/script contains any compile-time or syntax errors.
|
||||
|
||||
Moving on, in case the requirement is to add the output to the file, use the ':read !' command. Here's an example:
|
||||
|
||||
```
|
||||
:read ! wc %
|
||||
```
|
||||
|
||||
The 'read' command inserts the output of the external command on a new line below the current line in the file being edited. If you want, you can also specify a particular line number - the output will be added after that particular line.
|
||||
|
||||
For example, the following command will add the output of 'wc' after the second line the file.
|
||||
|
||||
```
|
||||
:2read ! wc %
|
||||
```
|
||||
|
||||
**Note**: Use '$' to insert after the last line and '0' to insert before the first line.
|
||||
|
||||
Now, coming back to the usecase we discussed in the beginning (**), here's the command that'll help you save the file without needing to close it first (which means no loss of unsaved changes) and then opening it with, say, [sudo][11].
|
||||
|
||||
```
|
||||
:w ! sudo tee %
|
||||
```
|
||||
|
||||
[
|
||||
![](https://www.howtoforge.com/images/how-to-access-shell-or-run-external-commands-from-within-vim/vim-sudo-passwrd.png)
|
||||
][12]
|
||||
|
||||
### Access Shell in Vim
|
||||
|
||||
In addition to executing individual commands, you can also have yourself dropped in a newly-launched shell from within Vim. For this, all you have to do is to run the following command from the editor:
|
||||
|
||||
```
|
||||
:shell
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```
|
||||
:sh
|
||||
```
|
||||
|
||||
and type 'exit' when you are done with the shell work - this will bring you back into the Vim session from where you left initially.
|
||||
|
||||
### The loophole to keep in mind
|
||||
|
||||
While the ability to access a shell definitely has its own uses in real world, it can also be used as a privilege escalation technique. As we have explained in one of our earlier tutorials (on sudoedit), even if you provide a user sudo access to only edit one file through Vim, they may launch a new shell from within the editor using this technique, and will then be able to do anything as 'root' or superuser.
|
||||
|
||||
# Conclusion
|
||||
|
||||
Ability to run external commands from within Vim is an important feature that can come in handy in many situations (some of them we have mentioned in this tutorial). The learning curve for this feature isn't steep, so both beginners as well as experienced users can take advantage of it.
|
||||
|
||||
Have you been using this feature for quite some time now? Do you have something to share? Please leave your thoughts in comments below.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.howtoforge.com/tutorial/how-to-access-shell-or-run-external-commands-from-within-vim/
|
||||
|
||||
作者:[Himanshu Arora][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/how-to-access-shell-or-run-external-commands-from-within-vim/
|
||||
[1]:https://www.howtoforge.com/tutorial/how-to-access-shell-or-run-external-commands-from-within-vim/#execute-external-commands-in-vim
|
||||
[2]:https://www.howtoforge.com/tutorial/how-to-access-shell-or-run-external-commands-from-within-vim/#access-shell-in-vim
|
||||
[3]:https://www.howtoforge.com/tutorial/how-to-access-shell-or-run-external-commands-from-within-vim/#the-loophole-to-keep-in-mind
|
||||
[4]:https://www.howtoforge.com/vim-basics
|
||||
[5]:https://www.howtoforge.com/tutorial/vim-editor-plugins-for-software-developers-3/
|
||||
[6]:https://www.howtoforge.com/tutorial/vim-modeline-settings/
|
||||
[7]:https://www.howtoforge.com/tutorial/vim-editor-modes-explained/
|
||||
[8]:https://www.howtoforge.com/images/how-to-access-shell-or-run-external-commands-from-within-vim/big/vim-perm-error.png
|
||||
[9]:https://www.howtoforge.com/images/how-to-access-shell-or-run-external-commands-from-within-vim/big/vim-count-lines.png
|
||||
[10]:https://www.howtoforge.com/images/how-to-access-shell-or-run-external-commands-from-within-vim/big/vim-wc-output.png
|
||||
[11]:https://www.howtoforge.com/tutorial/sudo-beginners-guide/
|
||||
[12]:https://www.howtoforge.com/images/how-to-access-shell-or-run-external-commands-from-within-vim/big/vim-sudo-passwrd.png
|
@ -1,3 +1,4 @@
|
||||
ucasFL translating
|
||||
Installation of Devuan Linux (Fork of Debian)
|
||||
============================================================
|
||||
|
||||
|
332
sources/tech/20170308 Our guide to a Golang logs world.md
Normal file
332
sources/tech/20170308 Our guide to a Golang logs world.md
Normal file
@ -0,0 +1,332 @@
|
||||
Our guide to a Golang logs world
|
||||
============================================================
|
||||
|
||||
![golang logo](https://logmatic.io/wp-content/uploads/2017/03/golang-logo.png)
|
||||
|
||||
Do you ever get tired of solutions that use convoluted languages, that are complex to deploy, and for which building takes forever? Golang is the solution to these very issues, being as fast as C and as simple as Python.
|
||||
|
||||
But how do you monitor your application with Golang logs? There are no exceptions in Golang, only errors. Your first impression might thus be that developing a Golang logging strategy is not going to be such a straightforward affair. The lack of exceptions is not in fact that troublesome, as exceptions have lost their exceptionality in many programming languages: they are often overused to the point of being overlooked.
|
||||
|
||||
We’ll first cover here Golang logging basics before going the extra mile and discuss Golang logs standardization, metadatas significance, and minimization of Golang logging impact on performance.
|
||||
By then, you’ll be able to track a user’s behavior across your application, quickly identify failing components in your project as well as monitor overall performance and user’s happiness.
|
||||
|
||||
### I. Basic Golang logging
|
||||
|
||||
### 1) Use Golang “log” library
|
||||
|
||||
Golang provides you with a native [logging library][3] simply called “log”. Its logger is perfectly suited to track simple behaviors such as adding a timestamp before an error message by using the available [flags][4].
|
||||
|
||||
Here is a basic example of how to log an error in Golang:
|
||||
|
||||
```
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func main() {
|
||||
/* local variable definition */
|
||||
...
|
||||
|
||||
/* function for division which return an error if divide by 0 */
|
||||
ret,err = div(a, b)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println(ret)
|
||||
}
|
||||
```
|
||||
|
||||
And here comes what you get if you try to divide by 0:
|
||||
|
||||
![golang code](https://logmatic.io/wp-content/uploads/2017/03/golang-code.png)
|
||||
|
||||
In order to quickly test a function in Golang you can use the [go playground][5].
|
||||
|
||||
To make sure your logs are easily accessible at all times, we recommend to write them in a file:
|
||||
|
||||
```
|
||||
package main
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
func main() {
|
||||
//create your file with desired read/write permissions
|
||||
f, err := os.OpenFile("filename", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
//defer to close when you're done with it, not because you think it's idiomatic!
|
||||
defer f.Close()
|
||||
//set output of logs to f
|
||||
log.SetOutput(f)
|
||||
//test case
|
||||
log.Println("check to make sure it works")
|
||||
}
|
||||
```
|
||||
|
||||
You can find a complete tutorial for Golang log library [here][6] and find the complete list of available functions within their “log” [library][7].
|
||||
|
||||
So now you should be all set to log errors and their root causes.
|
||||
|
||||
But logs can also help you piece an activity stream together, identify an error context that needs fixing or investigate how a single request is impacting several layers and API’s in your system.
|
||||
And to get this enhanced type of vision, you first need to enrich your Golang logs with as much context as possible as well as standardize the format you use across your project. This is when Golang native library reaches its limits. The most widely used libraries are then [glog][8] and [logrus][9]. It must be said though that many good libraries are available. So if you’re already using one that uses JSON format you don’t necessarily have to change library, as we’ll explain just below.
|
||||
|
||||
### II. A consistent format for your Golang logs
|
||||
|
||||
### 1) The structuring advantage of JSON format
|
||||
|
||||
Structuring your Golang logs in one project or across multiples microservices is probably the hardest part of the journey, even though it _could_ seem trivial once done. Structuring your logs is what makes them especially readable by machines (cf our [collecting logs best practices blogpost][10]). Flexibility and hierarchy are at the very core of the JSON format, so information can be easily parsed and manipulated by humans as well as by machines.
|
||||
|
||||
Here is an example of how to log in JSON format with the [Logrus/Logmatic.io][11] library:
|
||||
|
||||
```
|
||||
package main
|
||||
import (
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/logmatic/logmatic-go"
|
||||
)
|
||||
func main() {
|
||||
// use JSONFormatter
|
||||
log.SetFormatter(&logmatic.JSONFormatter{})
|
||||
// log an event as usual with logrus
|
||||
log.WithFields(log.Fields{"string": "foo", "int": 1, "float": 1.1 }).Info("My first ssl event from golang")
|
||||
}
|
||||
```
|
||||
|
||||
Which comes out as:
|
||||
|
||||
```
|
||||
{
|
||||
"date":"2016-05-09T10:56:00+02:00",
|
||||
"float":1.1,
|
||||
"int":1,
|
||||
"level":"info",
|
||||
"message":"My first ssl event from golang",
|
||||
"String":"foo"
|
||||
}
|
||||
```
|
||||
|
||||
### 2) Standardization of Golang logs
|
||||
|
||||
It really is a shame when the same error encountered in different parts of your code is registered differently in logs. Picture for example not being able to determine a web page loading status because of an error on one variable. One developer logged:
|
||||
|
||||
```
|
||||
message: 'unknown error: cannot determine loading status from unknown error: missing or invalid arg value client'</span>
|
||||
```
|
||||
|
||||
While the other registered:
|
||||
|
||||
```
|
||||
unknown error: cannot determine loading status - invalid client</span>
|
||||
```
|
||||
|
||||
A good solution to enforce logs standardization is to create an interface between your code and the logging library. This standardization interface would contain pre-defined log messages for all possible behavior you want to add in your logs. Doing so prevent custom log messages that would not match your desired standard format…. And in so doing facilitates log investigation.
|
||||
|
||||
![interface function](https://logmatic.io/wp-content/uploads/2017/03/functions-interface.png)
|
||||
|
||||
As log formats are centralized it becomes way easier to keep them up to date. If a new type of issue arises it only requires to be added in one interface for every team member to use the exact same message.
|
||||
|
||||
The most basic example would be to add the logger name and id before Golang log messages. Your code would then send “events” to your standardization interface that would in turn transform them into Golang log messages.
|
||||
|
||||
The most basic example would be to add the logger name and the id before the Golang log message. Your code would then send “events” to this interface that would transform them into Golang log messages:
|
||||
|
||||
```
|
||||
// The main part, we define all messages right here.
|
||||
// The Event struct is pretty simple. We maintain an Id to be sure to
|
||||
// retrieve simply all messages once they are logged
|
||||
var (
|
||||
invalidArgMessage = Event{1, "Invalid arg: %s"}
|
||||
invalidArgValueMessage = Event{2, "Invalid arg value: %s => %v"}
|
||||
missingArgMessage = Event{3, "Missing arg: %s"}
|
||||
)
|
||||
|
||||
// And here we were, all log events that can be used in our app
|
||||
func (l *Logger)InvalidArg(name string) {
|
||||
l.entry.Errorf(invalidArgMessage.toString(), name)
|
||||
}
|
||||
func (l *Logger)InvalidArgValue(name string, value interface{}) {
|
||||
l.entry.WithField("arg." + name, value).Errorf(invalidArgValueMessage.toString(), name, value)
|
||||
}
|
||||
func (l *Logger)MissingArg(name string) {
|
||||
l.entry.Errorf(missingArgMessage.toString(), name)
|
||||
}
|
||||
```
|
||||
|
||||
So if we use the previous example of the invalid argument value, we would get similar log messages:
|
||||
|
||||
```
|
||||
time="2017-02-24T23:12:31+01:00" level=error msg="LoadPageLogger00003 - Missing arg: client - cannot determine loading status" arg.client=<nil> logger.name=LoadPageLogger
|
||||
```
|
||||
|
||||
And in JSON format:
|
||||
|
||||
```
|
||||
{"arg.client":null,"level":"error","logger.name":"LoadPageLogger","msg":"LoadPageLogger00003 - Missing arg: client - cannot determine loading status", "time":"2017-02-24T23:14:28+01:00"}
|
||||
```
|
||||
|
||||
### III. The power of context in Golang logs
|
||||
|
||||
Now that the Golang logs are written in a structured and standardized format, time has come to decide which context and other relevant information should be added to them. Context and metadatas are critical in order to be able to extract insights from your logs such as following a user activity or its workflow.
|
||||
|
||||
For instance the Hostname, appname and session parameters could be added as follows using the JSON format of the logrus library:
|
||||
|
||||
```
|
||||
// For metadata, a common pattern is to re-use fields between logging statements by re-using
|
||||
contextualizedLog := log.WithFields(log.Fields{
|
||||
"hostname": "staging-1",
|
||||
"appname": "foo-app",
|
||||
"session": "1ce3f6v"
|
||||
})
|
||||
contextualizedLog.Info("Simple event with global metadata")
|
||||
```
|
||||
|
||||
Metadatas can be seen as javascript breadcrumbs. To better illustrate how important they are, let’s have a look at the use of metadatas among several Golang microservices. You’ll clearly see how decisive it is to track users on your application. This is because you do not simply need to know that an error occurred, but also on which instance and what pattern created the error. So let’s imagine we have two microservices which are sequentially called. The contextual information is transmitted and stored in the headers:
|
||||
|
||||
```
|
||||
func helloMicroService1(w http.ResponseWriter, r *http.Request) {
|
||||
client := &http.Client{}
|
||||
// This service is responsible to received all incoming user requests
|
||||
// So, we are checking if it's a new user session or a another call from
|
||||
// an existing session
|
||||
session := r.Header.Get("x-session")
|
||||
if ( session == "") {
|
||||
session = generateSessionId()
|
||||
// log something for the new session
|
||||
}
|
||||
// Track Id is unique per request, so in each case we generate one
|
||||
track := generateTrackId()
|
||||
// Call your 2nd microservice, add the session/track
|
||||
reqService2, _ := http.NewRequest("GET", "http://localhost:8082/", nil)
|
||||
reqService2.Header.Add("x-session", session)
|
||||
reqService2.Header.Add("x-track", track)
|
||||
resService2, _ := client.Do(reqService2)
|
||||
….
|
||||
```
|
||||
|
||||
So when the second service is called:
|
||||
|
||||
```
|
||||
func helloMicroService2(w http.ResponseWriter, r *http.Request) {
|
||||
// Like for the microservice, we check the session and generate a new track
|
||||
session := r.Header.Get("x-session")
|
||||
track := generateTrackId()
|
||||
// This time, we check if a track id is already set in the request,
|
||||
// if yes, it becomes the parent track
|
||||
parent := r.Header.Get("x-track")
|
||||
if (session == "") {
|
||||
w.Header().Set("x-parent", parent)
|
||||
}
|
||||
// Add meta to the response
|
||||
w.Header().Set("x-session", session)
|
||||
w.Header().Set("x-track", track)
|
||||
if (parent == "") {
|
||||
w.Header().Set("x-parent", track)
|
||||
}
|
||||
// Write the response body
|
||||
w.WriteHeader(http.StatusOK)
|
||||
io.WriteString(w, fmt.Sprintf(aResponseMessage, 2, session, track, parent))
|
||||
}
|
||||
```
|
||||
|
||||
Context and information relative to the initial query are now available in the second microservice and a log message in JSON format looks like the following ones:
|
||||
|
||||
In the first micro service:
|
||||
|
||||
```
|
||||
{"appname":"go-logging","level":"debug","msg":"hello from ms 1","session":"eUBrVfdw","time":"2017-03-02T15:29:26+01:00","track":"UzWHRihF"}
|
||||
```
|
||||
|
||||
Then in the second:
|
||||
|
||||
```
|
||||
{"appname":"go-logging","level":"debug","msg":"hello from ms 2","parent":"UzWHRihF","session":"eUBrVfdw","time":"2017-03-02T15:29:26+01:00","track":"DPRHBMuE"}
|
||||
```
|
||||
|
||||
In the case of an error occurring in the second micro service, we are now able – thanks to the contextual information hold in the Golang logs – to determine how it was called and what pattern created the error.
|
||||
|
||||
If you wish to dig deeper on Golang tracking possibilities, there are several libraries that offer tracking features such as [Opentracing][12]. This specific library delivers an easy way to add tracing implementations in complex (or simple) architecture. It allows you to track user queries across the different steps of any process as done below:
|
||||
|
||||
![client transaction](https://logmatic.io/wp-content/uploads/2017/03/client-transaction.png)
|
||||
|
||||
### IV. Performance impact of Golang logging
|
||||
|
||||
### 1) Do not log in Gorountine
|
||||
|
||||
It is tempting to create a new logger per goroutine. But it should not be done. Goroutine is a lightweight thread manager and is used to accomplish a “simple” task. It should not therefore be in charge of logging. It could lead to concurrency issues as using log.New() in each goroutine would duplicate the interface and all loggers would concurrently try to access the same io.Writer.
|
||||
Moreover libraries usually use a specific goroutine for the log writing to limit the impact on your performances and avoid concurrencial calls to the io.Writer.
|
||||
|
||||
### 2) Work with asynchronous libraries
|
||||
|
||||
If it is true that many Golang logging libraries are available, it’s important to note that most of them are synchronous (pseudo asynchronous in fact). The reason for this being probably that so far no one had any serious impact on their performance due to logging.
|
||||
|
||||
But as Kjell Hedström showed in [his experiment][13] using several threads that created millions of logs, asynchronous Golang logging could lead to 40% performance increase in the worst case scenario. So logging comes at a cost, and can have consequences on your application performance. In case you do not handle such volume of logs, using pseudo asynchronous Golang logging library might be efficient enough. But if you’re dealing with large amounts of logs or are keen on performance, Kjell Hedström asynchronous solution is interesting (despite the fact that you would probably have to develop it a bit as it only contains the minimum required features).
|
||||
|
||||
### 3) Use severity levels to manage your Golang logs volume
|
||||
|
||||
Some logging libraries allow you to enable or disable specific loggers, which can come in handy. You might not need some specific levels of logs once in production for example. Here is an example of how to disable a logger in the glog library where loggers are defined as boolean:
|
||||
|
||||
```
|
||||
type Log bool
|
||||
func (l Log) Println(args ...interface{}) {
|
||||
fmt.Println(args...)
|
||||
}
|
||||
var debug Log = false
|
||||
if debug {
|
||||
debug.Println("DEBUGGING")
|
||||
}
|
||||
```
|
||||
|
||||
You can then define those boolean parameters in a configuration file and use them to enable or disable loggers.
|
||||
|
||||
Golang logging can be expensive without a good Golang logging strategy. Developers should resist to the temptation of logging almost everything – even if much is interesting! If the purpose of logging is to gather as much information as possible, it has to be done properly in order to avoid the white noise of logs containing useless elements.
|
||||
|
||||
### V. Centralize Golang logs
|
||||
|
||||
![centralize go logs](https://logmatic.io/wp-content/uploads/2017/03/source-selector-1024x460-1.png)
|
||||
If your application is deployed on several servers, the hassle of connecting to each one of them to investigate a phenomenon can be avoided. Log centralization does make a difference.
|
||||
|
||||
Using log shippers such as Nxlog for windows, Rsyslog for linux (as it is installed by default) or Logstash and FluentD is the best way to do so. Log shippers only purpose is to send logs, and so they manage connection failures or other issues you could face very well.
|
||||
|
||||
There is even a [Golang syslog package][14] that takes care of sending Golang logs to the syslog daemon for you.
|
||||
|
||||
### Hope you enjoyed your Golang logs tour
|
||||
|
||||
Thinking about your Golang logging strategy at the beginning of your project is important. Tracking a user is much easier if overall context can be accessed from anywhere in the code. Reading logs from different services when they are not standardized is painful. Planning ahead to spread the same user or request id through several microservices will later on allow you to easily filter the information and follow an activity across your system.
|
||||
|
||||
Whether you’re building a large Golang project or several microservices also impacts your logging strategy. The main components of a large project should have their specific Golang logger named after their functionality. This enables you to instantly spot from which part of the code the logs are coming from. However with microservices or small Golang projects, fewer core components require their own logger. In each case though, the number of loggers should be kept below the number of core functionalities.
|
||||
|
||||
You’re now all set to quantify decisions about performance and user’s happiness with your Golang logs!
|
||||
|
||||
_Is there a specific coding language you want to read about? Let us know on Twitter [][1][@logmatic][2]._
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://logmatic.io/blog/our-guide-to-a-golang-logs-world/
|
||||
|
||||
作者:[Nils][a]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:https://logmatic.io/blog/our-guide-to-a-golang-logs-world/
|
||||
[1]:https://twitter.com/logmatic?lang=en
|
||||
[2]:http://twitter.com/logmatic
|
||||
[3]:https://golang.org/pkg/log/
|
||||
[4]:https://golang.org/pkg/log/#pkg-constants
|
||||
[5]:https://play.golang.org/
|
||||
[6]:https://www.goinggo.net/2013/11/using-log-package-in-go.html
|
||||
[7]:https://golang.org/pkg/log/
|
||||
[8]:https://github.com/google/glog
|
||||
[9]:https://github.com/sirupsen/logrus
|
||||
[10]:https://logmatic.io/blog/beyond-application-monitoring-discover-logging-best-practices/
|
||||
[11]:https://github.com/logmatic/logmatic-go
|
||||
[12]:https://github.com/opentracing/opentracing-go
|
||||
[13]:https://sites.google.com/site/kjellhedstrom2/g2log-efficient-background-io-processign-with-c11/g2log-vs-google-s-glog-performance-comparison
|
||||
[14]:https://golang.org/pkg/log/syslog/
|
568
sources/tech/20170312 OpenGL Go Tutorial Part 1.md
Normal file
568
sources/tech/20170312 OpenGL Go Tutorial Part 1.md
Normal file
@ -0,0 +1,568 @@
|
||||
OpenGL & Go Tutorial Part 1: Hello, OpenGL
|
||||
============================================================
|
||||
|
||||
_[Part 1: Hello, OpenGL][6]_ | _[Part 2: Drawing the Game Board][7]_ | _[Part 3: Implementing the Game][8]_
|
||||
|
||||
_The full source code of the tutorial is available on [GitHub][9]._
|
||||
|
||||
### Introduction
|
||||
|
||||
[OpenGL][19] is pretty much the gold standard for any kind of graphics work, from desktop GUIs to games to mobile applications and even the web, I can almost guarantee you’ve viewed something rendered by OpenGL today. However, regardless of how popular and useful OpenGL is, it can be quite intimidating to get started compared to more high-level graphics libraries.
|
||||
|
||||
The purpose of this tutorial is to give you a starting point and basic understanding of OpenGL, and how to utilize it with [Go][20]. There are bindings for OpenGL in just about every language and Go is no exception with the [go-gl][21] packages, a full suite of generated OpenGL bindings for various OpenGL versions.
|
||||
|
||||
The tutorial will walk through a few phases outlined below, with our end goal being to implement [Conway’s Game of Life][22] using OpenGL to draw the game board in a desktop window. The full source code is available on GitHub at [github.com/KyleBanks/conways-gol][23] so feel free to check it out if you get stuck or use it as a reference if you decide to go your own way.
|
||||
|
||||
Before we get started, we need to get an understanding of what _Conway’s Game of Life_ actually is. Here’s the summary from [Wikipedia][24]:
|
||||
|
||||
> The Game of Life, also known simply as Life, is a cellular automaton devised by the British mathematician John Horton Conway in 1970.
|
||||
>
|
||||
> The “game” is a zero-player game, meaning that its evolution is determined by its initial state, requiring no further input. One interacts with the Game of Life by creating an initial configuration and observing how it evolves, or, for advanced “players”, by creating patterns with particular properties.
|
||||
>
|
||||
> Rules
|
||||
>
|
||||
> The universe of the Game of Life is an infinite two-dimensional orthogonal grid of square cells, each of which is in one of two possible states, alive or dead, or “populated” or “unpopulated” (the difference may seem minor, except when viewing it as an early model of human/urban behaviour simulation or how one views a blank space on a grid). Every cell interacts with its eight neighbours, which are the cells that are horizontally, vertically, or diagonally adjacent. At each step in time, the following transitions occur:
|
||||
>
|
||||
> 1. Any live cell with fewer than two live neighbours dies, as if caused by underpopulation.
|
||||
> 2. Any live cell with two or three live neighbours lives on to the next generation.
|
||||
> 3. Any live cell with more than three live neighbours dies, as if by overpopulation.
|
||||
> 4. Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.
|
||||
|
||||
And without further ado, here’s a demo of what we’ll be building:
|
||||
|
||||
![Conway's Game of Life in OpenGL and Golang Tutorial - Demo Game](https://kylewbanks.com/images/post/golang-opengl-conway-1.gif)
|
||||
|
||||
In our simulation, a white cell indicates that it is alive, and black cell indicates that it is not.
|
||||
|
||||
### Outline
|
||||
|
||||
The tutorial is going to cover a lot of ground starting with the basics, however it will assume you have a minimal working knowledge of Go - at the very least you should know the basics of variables, slices, functions and structs, and have a working Go environment setup. I’ve developed the tutorial using Go version 1.8, but it should be compatible with previous versions as well. There is nothing particularly novel here in the Go implementation, so if you have experience in any similar programming language you should be just fine.
|
||||
|
||||
As for the tutorial, here’s what we’ll be covering:
|
||||
|
||||
* [Part 1: Hello, OpenGL][10]: Install and Setup OpenGL and [GLFW][11], Draw a Triangle to the Window
|
||||
* [Part 2: Drawing the Game Board][12]: Make a Square out of Triangles, Draw a Grid of Squares covering the Window
|
||||
* [Part 3: Implementing the Game][13]: Implement Conway’s Game
|
||||
|
||||
The final source code is available on [GitHub][25] but each _Part_ includes a _Checkpoint_ at the bottom containing the code up until that point. If anything is ever unclear or if you feel lost, check the bottom of the post for the full source!
|
||||
|
||||
Let’s get started!
|
||||
|
||||
### Install and Setup OpenGL and GLFW
|
||||
|
||||
We’ve introduced OpenGL but in order to use it we’re going to need a window to draw on to. [GLFW][26] is a cross-platform API for OpenGL that allows us to create and reference a window, and is also provided by the [go-gl][27] suite.
|
||||
|
||||
The first thing we need to do is decide on an OpenGL version. For the purposes of this tutorial we’ll use **OpenGL v4.1** but you can use **v2.1** just fine if your system doesn’t have the latest OpenGL versions. In order to install OpenGL we’ll do the following:
|
||||
|
||||
```
|
||||
# For OpenGL 4.1
|
||||
$ go get github.com/go-gl/gl/v4.1-core/gl
|
||||
|
||||
# Or 2.1
|
||||
$ go get github.com/go-gl/gl/v2.1/gl
|
||||
```
|
||||
|
||||
Next up, let’s install GLFW:
|
||||
|
||||
```
|
||||
$ go get github.com/go-gl/glfw/v3.2/glfw
|
||||
```
|
||||
|
||||
With these two packages installed, we’re ready to get started! We’re going to start by creating **main.go** and importing the packages (and a couple others we’ll need in a moment).
|
||||
|
||||
```
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"runtime"
|
||||
|
||||
"github.com/go-gl/gl/v4.1-core/gl" // OR: github.com/go-gl/gl/v2.1/gl
|
||||
"github.com/go-gl/glfw/v3.2/glfw"
|
||||
)
|
||||
```
|
||||
|
||||
Next lets define the **main** function, the purpose of which is to initialize OpenGL and GLFW and display the window:
|
||||
|
||||
```
|
||||
const (
|
||||
width = 500
|
||||
height = 500
|
||||
)
|
||||
|
||||
func main() {
|
||||
runtime.LockOSThread()
|
||||
|
||||
window := initGlfw()
|
||||
defer glfw.Terminate()
|
||||
|
||||
for !window.ShouldClose() {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
// initGlfw initializes glfw and returns a Window to use.
|
||||
func initGlfw() *glfw.Window {
|
||||
if err := glfw.Init(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
glfw.WindowHint(glfw.Resizable, glfw.False)
|
||||
glfw.WindowHint(glfw.ContextVersionMajor, 4) // OR 2
|
||||
glfw.WindowHint(glfw.ContextVersionMinor, 1)
|
||||
glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile)
|
||||
glfw.WindowHint(glfw.OpenGLForwardCompatible, glfw.True)
|
||||
|
||||
window, err := glfw.CreateWindow(width, height, "Conway's Game of Life", nil, nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
window.MakeContextCurrent()
|
||||
|
||||
return window
|
||||
}
|
||||
```
|
||||
|
||||
Alright let’s take a minute to walk through this and see what’s going on. First we define a couple constants, **width** and **height** - these will determine the size of the window, in pixels.
|
||||
|
||||
Next we have the **main** function. Here we instruct the **runtime** package to **LockOSThread()**, which ensures we will always execute in the same operating system thread, which is important for GLFW which must always be called from the same thread it was initialized on. Speaking of which, next we call **initGlfw** to get a window reference, and defer terminating. The window reference is then used in a for-loop where we say as long as the window should remain open, do _something_ . We’ll come back to this in a bit.
|
||||
|
||||
**initGlfw** is our next function, wherein we call **glfw.Init()** to initialize the GLFW package. After that, we define some global GLFW properties, including disabling window resizing and the properties of our OpenGL version. Next it’s time to create a **glfw.Window** which is where we’re going to do our future drawing. We simply tell it the width and height we want, as well as a title, and then call **window.MakeContextCurrent**, binding the window to our current thread. Finally, we return the window.
|
||||
|
||||
If you build and run the program now, you should see… nothing. This makes sense, because we’re not actually doing anything with the window yet.
|
||||
|
||||
Let’s fix that by defining a new function that initializes OpenGL:
|
||||
|
||||
```
|
||||
// initOpenGL initializes OpenGL and returns an intiialized program.
|
||||
func initOpenGL() uint32 {
|
||||
if err := gl.Init(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
version := gl.GoStr(gl.GetString(gl.VERSION))
|
||||
log.Println("OpenGL version", version)
|
||||
|
||||
prog := gl.CreateProgram()
|
||||
gl.LinkProgram(prog)
|
||||
return prog
|
||||
}
|
||||
```
|
||||
|
||||
**initOpenGL**, like our **initGlfw** function above, initializes the OpenGL library and creates a _program_ . A program gives us a reference to store shaders, which can then be used for drawing. We’ll come back to this in a bit, but for now just know that OpenGL is initialized and we have a **program** reference. We also print out the OpenGL version which can be helpful for debugging.
|
||||
|
||||
Back in **main**, let’s call this new function:
|
||||
|
||||
```
|
||||
func main() {
|
||||
runtime.LockOSThread()
|
||||
|
||||
window := initGlfw()
|
||||
defer glfw.Terminate()
|
||||
|
||||
program := initOpenGL()
|
||||
|
||||
for !window.ShouldClose() {
|
||||
draw(window, program)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You’ll notice now that we have our **program** reference, we’re calling a new **draw** function within our core window loop. Eventually this function will draw all of our cells to visualize the game state, but for now its just going to clear the window so we get a black screen:
|
||||
|
||||
```
|
||||
func draw(window *glfw.Window, program uint32) {
|
||||
gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
|
||||
gl.UseProgram(prog)
|
||||
|
||||
glfw.PollEvents()
|
||||
window.SwapBuffers()
|
||||
}
|
||||
```
|
||||
|
||||
The first thing we do is call **gl.Clear** to remove anything from the window that was drawn last frame, giving us a clean slate. Next we tell OpenGL to use our program reference, which currently does nothing. Finally, we tell GLFW to check if there were any mouse or keyboard events (which we won’t be handling in this tutorial) with the **PollEvents** function, and tell the window to **SwapBuffers**. [Buffer swapping][28] is important because GLFW (like many graphics libraries) uses double buffering, meaning everything you draw is actually drawn to an invisible canvas, and only put onto the visible canvas when you’re ready - which in this case, is indicated by calling **SwapBuffers**.
|
||||
|
||||
Alright, we’ve covered a lot here, so let’s take a moment to see the fruits of our labors. Go ahead and run the program now, and you should get to see your first visual:
|
||||
|
||||
![Conway's Game of Life in OpenGL and Golang Tutorial - First Window](https://kylewbanks.com/images/post/golang-opengl-conway-2.png)
|
||||
|
||||
Beautiful.
|
||||
|
||||
### Draw a Triangle to the Window
|
||||
|
||||
We’ve made some serious progress, even if it doesn’t look like much, but we still need to actually draw something. We’ll start by drawing a triangle, which may at first seem like it would be more difficult to draw than the squares we’re eventually going to, but you’d be mistaken for thinking so. What you may not know is that triangles are probably the easiest shapes to draw, and in fact we’ll eventually be making our squares out of triangles anyways.
|
||||
|
||||
Alright so we want to draw a triangle, but how? Well, we draw shapes by defining the vertices of the shapes and providing them to OpenGL to be drawn. Let’s first define our triangle at the top of **main.go**:
|
||||
|
||||
```
|
||||
var (
|
||||
triangle = []float32{
|
||||
0, 0.5, 0, // top
|
||||
-0.5, -0.5, 0, // left
|
||||
0.5, -0.5, 0, // right
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
This looks weird, but let’s break it down. First we have a slice of **float32**, which is the datatype we always use when providing vertices to OpenGL. The slice contains 9 values, three for each vertex of a triangle. The top line, **0, 0.5, 0**, is the top vertex represented as X, Y, and Z coordinates, the second line is the left vertex, and the third line is the right vertex. Each of these pairs of three represents the X, Y, and Z coordinates of the vertex relative to the center of the window, between **-1 and 1**. So the top point has an X of zero because its X is in the center of the window, a Y of _0.5_ meaning it will be up one quarter (because the range is -1 to 1) of the window relative to the center of the window, and a Z of zero. For our purposes because we are drawing only in two dimensions, our Z values will always be zero. Now have a look at the left and right vertices and see if you can understand why they are defined as they are - it’s okay if it isn’t immediately clear, we’re going to see it on the screen soon enough so we’ll have a perfect visualization to play with.
|
||||
|
||||
Okay, we have a triangle defined, but now we need to draw it. In order to draw it, we need what’s called a **Vertex Array Object** or **vao** which is created from a set of points (what we defined as our triangle), and can be provided to OpenGL to draw. Let’s create a function called **makeVao** that we can provide with a slice of points and have it return a pointer to an OpenGL vertex array object:
|
||||
|
||||
```
|
||||
// makeVao initializes and returns a vertex array from the points provided.
|
||||
func makeVao(points []float32) uint32 {
|
||||
var vbo uint32
|
||||
gl.GenBuffers(1, &vbo)
|
||||
gl.BindBuffer(gl.ARRAY_BUFFER, vbo)
|
||||
gl.BufferData(gl.ARRAY_BUFFER, 4*len(points), gl.Ptr(points), gl.STATIC_DRAW)
|
||||
|
||||
var vao uint32
|
||||
gl.GenVertexArrays(1, &vao)
|
||||
gl.BindVertexArray(vao)
|
||||
gl.EnableVertexAttribArray(0)
|
||||
gl.BindBuffer(gl.ARRAY_BUFFER, vbo)
|
||||
gl.VertexAttribPointer(0, 3, gl.FLOAT, false, 0, nil)
|
||||
|
||||
return vao
|
||||
}
|
||||
```
|
||||
|
||||
First we create a **Vertex Buffer Object** or **vbo** to bind our **vao** to, which is created by providing the size (**4 x len(points)**) and a pointer to the points (**gl.Ptr(points)**). You may be wondering why it’s **4 x len(points)** - why not 6 or 3 or 1078? The reason is we are using **float32** slices, and a 32-bit float has 4 bytes, so we are saying the size of the buffer, in bytes, is 4 times the number of points.
|
||||
|
||||
Now that we have a buffer, we can create the **vao** and bind it to the buffer with **gl.BindBuffer**, and finally return the **vao**. This **vao** will then be used to draw the triangle!
|
||||
|
||||
Back in **main**:
|
||||
|
||||
```
|
||||
func main() {
|
||||
...
|
||||
|
||||
vao := makeVao(triangle)
|
||||
for !window.ShouldClose() {
|
||||
draw(vao, window, program)
|
||||
}
|
||||
}
|
||||
|
||||
Here we call **makeVao** to get our **vao** reference from the **triangle** points we defined before, and pass it as a new argument to the **draw** function:
|
||||
|
||||
func draw(vao uint32, window *glfw.Window, program uint32) {
|
||||
gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
|
||||
gl.UseProgram(program)
|
||||
|
||||
gl.BindVertexArray(vao)
|
||||
gl.DrawArrays(gl.TRIANGLES, 0, int32(len(triangle) / 3))
|
||||
|
||||
glfw.PollEvents()
|
||||
window.SwapBuffers()
|
||||
}
|
||||
```
|
||||
|
||||
Then we bind OpenGL to our **vao** so it knows what we’re talking above when we tell it to **DrawArrays**, and tell it the length of the triangle (divided by three, one for each X, Y, Z coordinate) slice so it knows how many vertices to draw.
|
||||
|
||||
If you run the application at this point you might be expecting to see a beautiful triangle in the center of the window, but unfortunately you would be mistaken. There’s still one thing left to do, you see we’ve told OpenGL that we want to draw a triangle, but we need to tell it _how_ to draw the triangle.
|
||||
|
||||
In order to do that, we need what are called fragment and vertex shaders, which are quite advanced and beyond the scope of this tutorial (and honestly, beyond the scope of my OpenGL knowledge), however [Harold Serrano on Quora][29] gives a wonderful explanation of what they are. All we absolutely need to understand for this application is that shaders are their own mini-programs (written in [OpenGL Shader Language or GLSL][30]) that manipulate vertices to be drawn by OpenGL, and are used to (for example) determine the color of a shape.
|
||||
|
||||
We start by adding two more imports and a function called **compileShader**:
|
||||
|
||||
```
|
||||
import (
|
||||
"strings"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func compileShader(source string, shaderType uint32) (uint32, error) {
|
||||
shader := gl.CreateShader(shaderType)
|
||||
|
||||
csources, free := gl.Strs(source)
|
||||
gl.ShaderSource(shader, 1, csources, nil)
|
||||
free()
|
||||
gl.CompileShader(shader)
|
||||
|
||||
var status int32
|
||||
gl.GetShaderiv(shader, gl.COMPILE_STATUS, &status)
|
||||
if status == gl.FALSE {
|
||||
var logLength int32
|
||||
gl.GetShaderiv(shader, gl.INFO_LOG_LENGTH, &logLength)
|
||||
|
||||
log := strings.Repeat("\x00", int(logLength+1))
|
||||
gl.GetShaderInfoLog(shader, logLength, nil, gl.Str(log))
|
||||
|
||||
return 0, fmt.Errorf("failed to compile %v: %v", source, log)
|
||||
}
|
||||
|
||||
return shader, nil
|
||||
}
|
||||
```
|
||||
|
||||
The purpose of this function is to receive the shader source code as a string as well as its type, and return a pointer to the resulting compiled shader. If it fails to compile we’ll get an error returned containing the details.
|
||||
|
||||
Now let’s define the shaders and compile them from **makeProgram**. Back up in our **const** block where we define **width** and **height**:
|
||||
|
||||
```
|
||||
vertexShaderSource = `
|
||||
#version 410
|
||||
in vec3 vp;
|
||||
void main() {
|
||||
gl_Position = vec4(vp, 1.0);
|
||||
}
|
||||
` + "\x00"
|
||||
|
||||
fragmentShaderSource = `
|
||||
#version 410
|
||||
out vec4 frag_colour;
|
||||
void main() {
|
||||
frag_colour = vec4(1, 1, 1, 1);
|
||||
}
|
||||
` + "\x00"
|
||||
```
|
||||
|
||||
As you can see these are strings containing GLSL source code for two shaders, one for a _vertex shader_ and another for a _fragment shader_ . The only thing special about these strings is that they both end in a null-termination character, **\x00** - a requirement for OpenGL to be able to compile them. Make note of the **fragmentShaderSource**, this is where we define the color of our shape in RGBA format using a **vec4**. You can change the value here, which is currently **RGBA(1, 1, 1, 1)** or _white_ , to change the color of the triangle.
|
||||
|
||||
Also of note is that both programs start with **#version 410**, which you should change to **#version 120** if using OpenGL 2.1. **120** is not a typo - use **120** not **210** if you’re on OpenGL 2.1!
|
||||
|
||||
Next in **initOpenGL** we’ll compile the shaders and attach them to our **program**:
|
||||
|
||||
```
|
||||
func initOpenGL() uint32 {
|
||||
if err := gl.Init(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
version := gl.GoStr(gl.GetString(gl.VERSION))
|
||||
log.Println("OpenGL version", version)
|
||||
|
||||
vertexShader, err := compileShader(vertexShaderSource, gl.VERTEX_SHADER)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fragmentShader, err := compileShader(fragmentShaderSource, gl.FRAGMENT_SHADER)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
prog := gl.CreateProgram()
|
||||
gl.AttachShader(prog, vertexShader)
|
||||
gl.AttachShader(prog, fragmentShader)
|
||||
gl.LinkProgram(prog)
|
||||
return prog
|
||||
}
|
||||
```
|
||||
|
||||
Here we call our **compileShader** function with the _vertex shader_ , specifying its type as a **gl.VERTEX_SHADER**, and do the same with the _fragment shader_ but specifying its type as a **gl.FRAGMENT_SHADER**. After compiling them, we attach them to our program by calling **gl.AttachShader** with our program and each compiled shader.
|
||||
|
||||
And now we’re finally ready to see our glorious triangle! Go ahead and run, and if all is well you’ll see:
|
||||
|
||||
![Conway's Game of Life in OpenGL and Golang Tutorial - Hello, Triangle!](https://kylewbanks.com/images/post/golang-opengl-conway-3.png)
|
||||
|
||||
### Summary
|
||||
|
||||
Amazing, right! All that for a single triangle, but I promise you we’ve setup the majority of the OpenGL code that will serve us for the rest of the tutorial. I highly encourage you to take a few minutes to play with the code and see if you can move, resize, and change the color of the triangle. OpenGL can be _very_ intimidating, and it can feel at times like its difficult to understand what’s going on, but understand that this isn’t magic - it only looks like it is.
|
||||
|
||||
In the next part of the tutorial we’ll make a square out of two right-angled triangles - see if you can try and figure this part out before moving on. If not, don’t worry because we’ll be walking through the code in [Part 2][31], followed by creating a full grid of squares that will act as our game board.
|
||||
|
||||
Finally, in [Part 3][32] we continue by using the grid to implement _Conway’s Game of Life!_
|
||||
|
||||
_[Part 1: Hello, OpenGL][14]_ | _[Part 2: Drawing the Game Board][15]_ | _[Part 3: Implementing the Game][16]_
|
||||
|
||||
_The full source code of the tutorial is available on [GitHub][17]._
|
||||
|
||||
### Checkpoint
|
||||
|
||||
Here’s the contents of **main.go** at this point of the tutorial:
|
||||
|
||||
```
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/go-gl/gl/v4.1-core/gl" // OR: github.com/go-gl/gl/v2.1/gl
|
||||
"github.com/go-gl/glfw/v3.2/glfw"
|
||||
)
|
||||
|
||||
const (
|
||||
width = 500
|
||||
height = 500
|
||||
|
||||
vertexShaderSource = `
|
||||
#version 410
|
||||
in vec3 vp;
|
||||
void main() {
|
||||
gl_Position = vec4(vp, 1.0);
|
||||
}
|
||||
` + "\x00"
|
||||
|
||||
fragmentShaderSource = `
|
||||
#version 410
|
||||
out vec4 frag_colour;
|
||||
void main() {
|
||||
frag_colour = vec4(1, 1, 1, 1.0);
|
||||
}
|
||||
` + "\x00"
|
||||
)
|
||||
|
||||
var (
|
||||
triangle = []float32{
|
||||
0, 0.5, 0,
|
||||
-0.5, -0.5, 0,
|
||||
0.5, -0.5, 0,
|
||||
}
|
||||
)
|
||||
|
||||
func main() {
|
||||
runtime.LockOSThread()
|
||||
|
||||
window := initGlfw()
|
||||
defer glfw.Terminate()
|
||||
program := initOpenGL()
|
||||
|
||||
vao := makeVao(triangle)
|
||||
for !window.ShouldClose() {
|
||||
draw(vao, window, program)
|
||||
}
|
||||
}
|
||||
|
||||
func draw(vao uint32, window *glfw.Window, program uint32) {
|
||||
gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
|
||||
gl.UseProgram(program)
|
||||
|
||||
gl.BindVertexArray(vao)
|
||||
gl.DrawArrays(gl.TRIANGLES, 0, int32(len(triangle)/3))
|
||||
|
||||
glfw.PollEvents()
|
||||
window.SwapBuffers()
|
||||
}
|
||||
|
||||
// initGlfw initializes glfw and returns a Window to use.
|
||||
func initGlfw() *glfw.Window {
|
||||
if err := glfw.Init(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
glfw.WindowHint(glfw.Resizable, glfw.False)
|
||||
glfw.WindowHint(glfw.ContextVersionMajor, 4)
|
||||
glfw.WindowHint(glfw.ContextVersionMinor, 1)
|
||||
glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile)
|
||||
glfw.WindowHint(glfw.OpenGLForwardCompatible, glfw.True)
|
||||
|
||||
window, err := glfw.CreateWindow(width, height, "Conway's Game of Life", nil, nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
window.MakeContextCurrent()
|
||||
|
||||
return window
|
||||
}
|
||||
|
||||
// initOpenGL initializes OpenGL and returns an intiialized program.
|
||||
func initOpenGL() uint32 {
|
||||
if err := gl.Init(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
version := gl.GoStr(gl.GetString(gl.VERSION))
|
||||
log.Println("OpenGL version", version)
|
||||
|
||||
vertexShader, err := compileShader(vertexShaderSource, gl.VERTEX_SHADER)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fragmentShader, err := compileShader(fragmentShaderSource, gl.FRAGMENT_SHADER)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
prog := gl.CreateProgram()
|
||||
gl.AttachShader(prog, vertexShader)
|
||||
gl.AttachShader(prog, fragmentShader)
|
||||
gl.LinkProgram(prog)
|
||||
return prog
|
||||
}
|
||||
|
||||
// makeVao initializes and returns a vertex array from the points provided.
|
||||
func makeVao(points []float32) uint32 {
|
||||
var vbo uint32
|
||||
gl.GenBuffers(1, &vbo)
|
||||
gl.BindBuffer(gl.ARRAY_BUFFER, vbo)
|
||||
gl.BufferData(gl.ARRAY_BUFFER, 4*len(points), gl.Ptr(points), gl.STATIC_DRAW)
|
||||
|
||||
var vao uint32
|
||||
gl.GenVertexArrays(1, &vao)
|
||||
gl.BindVertexArray(vao)
|
||||
gl.EnableVertexAttribArray(0)
|
||||
gl.BindBuffer(gl.ARRAY_BUFFER, vbo)
|
||||
gl.VertexAttribPointer(0, 3, gl.FLOAT, false, 0, nil)
|
||||
|
||||
return vao
|
||||
}
|
||||
|
||||
func compileShader(source string, shaderType uint32) (uint32, error) {
|
||||
shader := gl.CreateShader(shaderType)
|
||||
|
||||
csources, free := gl.Strs(source)
|
||||
gl.ShaderSource(shader, 1, csources, nil)
|
||||
free()
|
||||
gl.CompileShader(shader)
|
||||
|
||||
var status int32
|
||||
gl.GetShaderiv(shader, gl.COMPILE_STATUS, &status)
|
||||
if status == gl.FALSE {
|
||||
var logLength int32
|
||||
gl.GetShaderiv(shader, gl.INFO_LOG_LENGTH, &logLength)
|
||||
|
||||
log := strings.Repeat("\x00", int(logLength+1))
|
||||
gl.GetShaderInfoLog(shader, logLength, nil, gl.Str(log))
|
||||
|
||||
return 0, fmt.Errorf("failed to compile %v: %v", source, log)
|
||||
}
|
||||
|
||||
return shader, nil
|
||||
}
|
||||
```
|
||||
Let me know if this post was helpful on Twitter [@kylewbanks][33] or down below, and follow me to keep up with future posts!
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://kylewbanks.com/blog/tutorial-opengl-with-golang-part-1-hello-opengl
|
||||
|
||||
作者:[kylewbanks ][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/kylewbanks
|
||||
[1]:https://kylewbanks.com/category/golang
|
||||
[2]:https://kylewbanks.com/category/opengl
|
||||
[3]:https://twitter.com/intent/tweet?text=OpenGL%20%26%20Go%20Tutorial%20Part%201%3A%20Hello%2C%20OpenGL%20https%3A%2F%2Fkylewbanks.com%2Fblog%2Ftutorial-opengl-with-golang-part-1-hello-opengl%20by%20%40kylewbanks
|
||||
[4]:mailto:?subject=Check%20Out%20%22OpenGL%20%26%20Go%20Tutorial%20Part%201%3A%20Hello%2C%20OpenGL%22&body=https%3A%2F%2Fkylewbanks.com%2Fblog%2Ftutorial-opengl-with-golang-part-1-hello-opengl
|
||||
[5]:https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fkylewbanks.com%2Fblog%2Ftutorial-opengl-with-golang-part-1-hello-opengl
|
||||
[6]:https://kylewbanks.com/blog/tutorial-opengl-with-golang-part-1-hello-opengl
|
||||
[7]:https://kylewbanks.com/blog/tutorial-opengl-with-golang-part-2-drawing-the-game-board
|
||||
[8]:https://kylewbanks.com/blog/tutorial-opengl-with-golang-part-3-implementing-the-game
|
||||
[9]:https://github.com/KyleBanks/conways-gol
|
||||
[10]:https://kylewbanks.com/blog/tutorial-opengl-with-golang-part-1-hello-opengl
|
||||
[11]:http://www.glfw.org/
|
||||
[12]:https://kylewbanks.com/blog/tutorial-opengl-with-golang-part-2-drawing-the-game-board
|
||||
[13]:https://kylewbanks.com/blog/blog/tutorial-opengl-with-golang-part-3-implementing-the-game
|
||||
[14]:https://kylewbanks.com/blog/tutorial-opengl-with-golang-part-1-hello-opengl
|
||||
[15]:https://kylewbanks.com/blog/tutorial-opengl-with-golang-part-2-drawing-the-game-board
|
||||
[16]:https://kylewbanks.com/blog/tutorial-opengl-with-golang-part-3-implementing-the-game
|
||||
[17]:https://github.com/KyleBanks/conways-gol
|
||||
[18]:https://twitter.com/kylewbanks
|
||||
[19]:https://www.opengl.org/
|
||||
[20]:https://golang.org/
|
||||
[21]:https://github.com/go-gl/gl
|
||||
[22]:https://en.wikipedia.org/wiki/Conway's_Game_of_Life
|
||||
[23]:https://github.com/KyleBanks/conways-gol
|
||||
[24]:https://en.wikipedia.org/wiki/Conway's_Game_of_Life
|
||||
[25]:https://github.com/KyleBanks/conways-gol
|
||||
[26]:http://www.glfw.org/
|
||||
[27]:https://github.com/go-gl/glfw
|
||||
[28]:http://www.glfw.org/docs/latest/window_guide.html#buffer_swap
|
||||
[29]:https://www.quora.com/What-is-a-vertex-shader-and-what-is-a-fragment-shader/answer/Harold-Serrano?srid=aVb
|
||||
[30]:https://www.opengl.org/sdk/docs/tutorials/ClockworkCoders/glsl_overview.php
|
||||
[31]:https://kylewbanks.com/blog/tutorial-opengl-with-golang-part-2-drawing-the-game-board
|
||||
[32]:https://kylewbanks.com/blog/tutorial-opengl-with-golang-part-3-implementing-the-game
|
||||
[33]:https://twitter.com/kylewbanks
|
@ -0,0 +1,511 @@
|
||||
OpenGL & Go Tutorial Part 2: Drawing the Game Board
|
||||
============================================================
|
||||
|
||||
_[Part 1: Hello, OpenGL][6]_ | _[Part 2: Drawing the Game Board][7]_ | _[Part 3: Implementing the Game][8]_
|
||||
|
||||
_The full source code of the tutorial is available on [GitHub][9]._
|
||||
|
||||
Welcome back to the _OpenGL & Go Tutorial!_ If you haven’t gone through [Part 1][15] you’ll definitely want to take a step back and check it out.
|
||||
|
||||
At this point you should be the proud creator of a magnificent white triangle, but we’re not in the business of using triangles as our game unit so it’s time to turn the triangle into a square, and then we’ll make an entire grid of them.
|
||||
|
||||
Let’s get started!
|
||||
|
||||
### Make a Square out of Triangles
|
||||
|
||||
Before we can make a square, let’s turn our triangle into a right-angle. Open up **main.go** and change the **triangle** definition to look like so:
|
||||
|
||||
```
|
||||
triangle = []float32{
|
||||
-0.5, 0.5, 0,
|
||||
-0.5, -0.5, 0,
|
||||
0.5, -0.5, 0,
|
||||
}
|
||||
```
|
||||
|
||||
What we’ve done is move the X-coordinate of the top vertex to the left (**-0.5**), giving us a triangle like so:
|
||||
|
||||
![Conway's Game of Life in OpenGL and Golang Tutorial - Right-Angle Triangle](https://kylewbanks.com/images/post/golang-opengl-conway-4.png)
|
||||
|
||||
Easy enough, right? Now let’s make a square out of two of these. Let’s rename **triangle** to **square** and add a second, inverted right-angle triangle to the slice:
|
||||
|
||||
```
|
||||
square = []float32{
|
||||
-0.5, 0.5, 0,
|
||||
-0.5, -0.5, 0,
|
||||
0.5, -0.5, 0,
|
||||
|
||||
-0.5, 0.5, 0,
|
||||
0.5, 0.5, 0,
|
||||
0.5, -0.5, 0,
|
||||
}
|
||||
```
|
||||
|
||||
Note: You’ll also need to rename the two references to **triangle** to be **square**, namely in **main** and **draw**.
|
||||
|
||||
Here we’ve doubled the number of points by adding a second set of three vertices to be our upper top-right triangle to complete the square. Run it for glory:
|
||||
|
||||
![Conway's Game of Life in OpenGL and Golang Tutorial - Two Triangles Make a Square](https://kylewbanks.com/images/post/golang-opengl-conway-5.png)
|
||||
|
||||
Great, now we have the ability to draw a square! OpenGL isn’t so tough after all, is it?
|
||||
|
||||
### Draw a Grid of Squares covering the Window
|
||||
|
||||
Now that we can draw one square, how about 100 of them? Let’s create a **cell** struct to represent each unit of our grid so that we can be flexible in the number of squares we draw:
|
||||
|
||||
```
|
||||
type cell struct {
|
||||
drawable uint32
|
||||
|
||||
x int
|
||||
y int
|
||||
}
|
||||
```
|
||||
|
||||
The **cell** contains a **drawable** which is a square **Vertex Array Object** just like the one we created above, and an X and Y coordinate to dictate where on the grid this cell resides.
|
||||
|
||||
We’re also going to want two more constants that define the size and shape of our grid:
|
||||
|
||||
```
|
||||
const (
|
||||
...
|
||||
|
||||
rows = 10
|
||||
columns = 10
|
||||
)
|
||||
```
|
||||
|
||||
Now let’s add a function to create the grid:
|
||||
|
||||
```
|
||||
func makeCells() [][]*cell {
|
||||
cells := make([][]*cell, rows, rows)
|
||||
for x := 0; x < rows; x++ {
|
||||
for y := 0; y < columns; y++ {
|
||||
c := newCell(x, y)
|
||||
cells[x] = append(cells[x], c)
|
||||
}
|
||||
}
|
||||
|
||||
return cells
|
||||
}
|
||||
```
|
||||
|
||||
Here we create a multi-dimensional slice to represent our game’s board, and populate each element of the matrix with a **cell** using a new function called **newCell** which we’ll write in just a moment.
|
||||
|
||||
Before moving on, let’s take a moment to visualize what **makeCells** is creating. We’re creating a slice that is equal in length to the number of rows on the grid, and each of these slices contains a slice of cells, equal in length to the number of columns. If we were to define **rows** and **columns** each equal to two, we’d create the following matrix:
|
||||
|
||||
```
|
||||
[
|
||||
[cell, cell],
|
||||
[cell, cell]
|
||||
]
|
||||
```
|
||||
|
||||
We’re creating a much larger matrix that’s **10x10** cells:
|
||||
|
||||
```
|
||||
[
|
||||
[cell, cell, cell, cell, cell, cell, cell, cell, cell, cell],
|
||||
[cell, cell, cell, cell, cell, cell, cell, cell, cell, cell],
|
||||
[cell, cell, cell, cell, cell, cell, cell, cell, cell, cell],
|
||||
[cell, cell, cell, cell, cell, cell, cell, cell, cell, cell],
|
||||
[cell, cell, cell, cell, cell, cell, cell, cell, cell, cell],
|
||||
[cell, cell, cell, cell, cell, cell, cell, cell, cell, cell],
|
||||
[cell, cell, cell, cell, cell, cell, cell, cell, cell, cell],
|
||||
[cell, cell, cell, cell, cell, cell, cell, cell, cell, cell],
|
||||
[cell, cell, cell, cell, cell, cell, cell, cell, cell, cell],
|
||||
[cell, cell, cell, cell, cell, cell, cell, cell, cell, cell]
|
||||
]
|
||||
```
|
||||
|
||||
Now that the we understand the shape and representation of the matrix we’re creating, let’s have a look at **newCell** which we use to actually populate the matrix:
|
||||
|
||||
```
|
||||
func newCell(x, y int) *cell {
|
||||
points := make([]float32, len(square), len(square))
|
||||
copy(points, square)
|
||||
|
||||
for i := 0; i < len(points); i++ {
|
||||
var position float32
|
||||
var size float32
|
||||
switch i % 3 {
|
||||
case 0:
|
||||
size = 1.0 / float32(columns)
|
||||
position = float32(x) * size
|
||||
case 1:
|
||||
size = 1.0 / float32(rows)
|
||||
position = float32(y) * size
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
||||
if points[i] < 0 {
|
||||
points[i] = (position * 2) - 1
|
||||
} else {
|
||||
points[i] = ((position + size) * 2) - 1
|
||||
}
|
||||
}
|
||||
|
||||
return &cell{
|
||||
drawable: makeVao(points),
|
||||
|
||||
x: x,
|
||||
y: y,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
There’s quite a lot going on in this function so let’s break it down. The first thing we do is create a copy of our **square** definition. This allows us to change its contents to customize the current cell’s position, without impacting any other cells that are also using the **square** slice. Next we iterate over the **points** copy and act based on the current index. We use a modulo operation to determine if we’re at an X (**i % 3 == 0**) or Y (**i % 3 == 1**) coordinate **of the shape** (skipping Z since we’re operating in two dimensions) and determine the size (as a percentage of the entire game board) of the cell accordingly, as well as it’s position based on the X and Y coordinate of the cell **on the game board**.
|
||||
|
||||
Next, we modify the points which currently contain a combination of **0.5**, **0** and **-0.5** as we defined them in the **square** slice. If the point is less than zero, we set it equal to the position times 2 (because OpenGL coordinates have a range of 2, between **-1** and **1**), minus 1 to normalize to OpenGL coordinates. If the position is greater than or equal to zero, we do the same thing but add the size we calculated.
|
||||
|
||||
The purpose of this is to set the scale of each cell so that it fills only its percentage of the game board. Since we have 10 rows and 10 columns, each cell will be given 10% of the width and 10% of the height of the game board.
|
||||
|
||||
Finally, after all the points have been scaled and positioned, we create a **cell** with the X and Y coordinate provided, and set the **drawable**field equal to a **Vertex Array Object** created from the **points** slice we just manipulated.
|
||||
|
||||
Alright, now in **main** we can remove our call to **makeVao** and replace it with a call to **makeCells**. We’ll also change **draw** to take the matrix of cells instead of a single **vao**:
|
||||
|
||||
```
|
||||
func main() {
|
||||
...
|
||||
|
||||
// vao := makeVao(square)
|
||||
cells := makeCells()
|
||||
|
||||
for !window.ShouldClose() {
|
||||
draw(cells, window, program)
|
||||
}
|
||||
}
|
||||
|
||||
func draw(cells [][]*cell, window *glfw.Window, program uint32) {
|
||||
gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
|
||||
gl.UseProgram(program)
|
||||
|
||||
// TODO
|
||||
|
||||
glfw.PollEvents()
|
||||
window.SwapBuffers()
|
||||
}
|
||||
```
|
||||
|
||||
Now we’ll need each cell to know how to draw itself. Let’s add a **draw** function to the **cell**:
|
||||
|
||||
```
|
||||
func (c *cell) draw() {
|
||||
gl.BindVertexArray(c.drawable)
|
||||
gl.DrawArrays(gl.TRIANGLES, 0, int32(len(square) / 3))
|
||||
}
|
||||
```
|
||||
|
||||
This should look familiar, its nearly identical to how we were drawing the square **vao** in **draw** previously, the only difference being we **BindVertexArray** using **c.drawable**, which is the cell’s **vao** we created in **newCell**.
|
||||
|
||||
Back in the main **draw** function, we can loop over each cell and have it draw itself:
|
||||
|
||||
```
|
||||
func draw(cells [][]*cell, window *glfw.Window, program uint32) {
|
||||
gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
|
||||
gl.UseProgram(program)
|
||||
|
||||
for x := range cells {
|
||||
for _, c := range cells[x] {
|
||||
c.draw()
|
||||
}
|
||||
}
|
||||
|
||||
glfw.PollEvents()
|
||||
window.SwapBuffers()
|
||||
}
|
||||
```
|
||||
|
||||
As you can see we loop over each of the cells and call its **draw** function. If you run the application you should see the following:
|
||||
|
||||
![Conway's Game of Life in OpenGL and Golang Tutorial - Full Grid](https://kylewbanks.com/images/post/golang-opengl-conway-6.png)
|
||||
|
||||
Is this what you expected? What we’ve done is create a square for each row and column on the grid, and colored it in, effectively filling the entire game board!
|
||||
|
||||
We can see an visualize individual cells by commenting out the for-loop for a moment and doing the following:
|
||||
|
||||
```
|
||||
// for x := range cells {
|
||||
// for _, c := range cells[x] {
|
||||
// c.draw()
|
||||
// }
|
||||
// }
|
||||
|
||||
cells[2][3].draw()
|
||||
```
|
||||
|
||||
![Conway's Game of Life in OpenGL and Golang Tutorial - A Single Cell](https://kylewbanks.com/images/post/golang-opengl-conway-7.png)
|
||||
|
||||
This draws only the cell located at coordinate **(X=2, Y=3)**. As you can see, each individual cell takes up a small portion of the game board, and is responsible for drawing its own space. We can also see that our game board has its origin, that is the **(X=0, Y=0)** coordinate, in the bottom-left corner of the window. This is simply a result of the way our **newCell** function calculates the position, and could be made to use the top-right, bottom-right, top-left, center, or any other position as its origin.
|
||||
|
||||
Let’s go ahead and remove the **cells[2][3].draw()** line and uncomment the for-loop, leaving us with the fully drawn grid we had above.
|
||||
|
||||
### Summary
|
||||
|
||||
Alright - we can now use two triangles to draw a square, and we have ourselves a game board! We should be proud, we’ve covered a lot of ground up to this point and to be completely honest, the hardest part is behind us now!
|
||||
|
||||
Next up in [Part 3][16] we’ll implement the core game logic and see some cool simulations!
|
||||
|
||||
_[Part 1: Hello, OpenGL][10]_ | _[Part 2: Drawing the Game Board][11]_ | _[Part 3: Implementing the Game][12]_
|
||||
|
||||
_The full source code of the tutorial is available on [GitHub][13]._
|
||||
|
||||
### Checkpoint
|
||||
|
||||
Here’s the contents of **main.go** at this point of the tutorial:
|
||||
|
||||
```
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/go-gl/gl/v4.1-core/gl" // OR: github.com/go-gl/gl/v2.1/gl
|
||||
"github.com/go-gl/glfw/v3.2/glfw"
|
||||
)
|
||||
|
||||
const (
|
||||
width = 500
|
||||
height = 500
|
||||
|
||||
vertexShaderSource = `
|
||||
#version 410
|
||||
in vec3 vp;
|
||||
void main() {
|
||||
gl_Position = vec4(vp, 1.0);
|
||||
}
|
||||
` + "\x00"
|
||||
|
||||
fragmentShaderSource = `
|
||||
#version 410
|
||||
out vec4 frag_colour;
|
||||
void main() {
|
||||
frag_colour = vec4(1, 1, 1, 1.0);
|
||||
}
|
||||
` + "\x00"
|
||||
|
||||
rows = 10
|
||||
columns = 10
|
||||
)
|
||||
|
||||
var (
|
||||
square = []float32{
|
||||
-0.5, 0.5, 0,
|
||||
-0.5, -0.5, 0,
|
||||
0.5, -0.5, 0,
|
||||
|
||||
-0.5, 0.5, 0,
|
||||
0.5, 0.5, 0,
|
||||
0.5, -0.5, 0,
|
||||
}
|
||||
)
|
||||
|
||||
type cell struct {
|
||||
drawable uint32
|
||||
|
||||
x int
|
||||
y int
|
||||
}
|
||||
|
||||
func main() {
|
||||
runtime.LockOSThread()
|
||||
|
||||
window := initGlfw()
|
||||
defer glfw.Terminate()
|
||||
program := initOpenGL()
|
||||
|
||||
cells := makeCells()
|
||||
for !window.ShouldClose() {
|
||||
draw(cells, window, program)
|
||||
}
|
||||
}
|
||||
|
||||
func draw(cells [][]*cell, window *glfw.Window, program uint32) {
|
||||
gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
|
||||
gl.UseProgram(program)
|
||||
|
||||
for x := range cells {
|
||||
for _, c := range cells[x] {
|
||||
c.draw()
|
||||
}
|
||||
}
|
||||
|
||||
glfw.PollEvents()
|
||||
window.SwapBuffers()
|
||||
}
|
||||
|
||||
func makeCells() [][]*cell {
|
||||
cells := make([][]*cell, rows, rows)
|
||||
for x := 0; x < rows; x++ {
|
||||
for y := 0; y < columns; y++ {
|
||||
c := newCell(x, y)
|
||||
cells[x] = append(cells[x], c)
|
||||
}
|
||||
}
|
||||
|
||||
return cells
|
||||
}
|
||||
|
||||
func newCell(x, y int) *cell {
|
||||
points := make([]float32, len(square), len(square))
|
||||
copy(points, square)
|
||||
|
||||
for i := 0; i < len(points); i++ {
|
||||
var position float32
|
||||
var size float32
|
||||
switch i % 3 {
|
||||
case 0:
|
||||
size = 1.0 / float32(columns)
|
||||
position = float32(x) * size
|
||||
case 1:
|
||||
size = 1.0 / float32(rows)
|
||||
position = float32(y) * size
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
||||
if points[i] < 0 {
|
||||
points[i] = (position * 2) - 1
|
||||
} else {
|
||||
points[i] = ((position + size) * 2) - 1
|
||||
}
|
||||
}
|
||||
|
||||
return &cell{
|
||||
drawable: makeVao(points),
|
||||
|
||||
x: x,
|
||||
y: y,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *cell) draw() {
|
||||
gl.BindVertexArray(c.drawable)
|
||||
gl.DrawArrays(gl.TRIANGLES, 0, int32(len(square)/3))
|
||||
}
|
||||
|
||||
// initGlfw initializes glfw and returns a Window to use.
|
||||
func initGlfw() *glfw.Window {
|
||||
if err := glfw.Init(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
glfw.WindowHint(glfw.Resizable, glfw.False)
|
||||
glfw.WindowHint(glfw.ContextVersionMajor, 4)
|
||||
glfw.WindowHint(glfw.ContextVersionMinor, 1)
|
||||
glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile)
|
||||
glfw.WindowHint(glfw.OpenGLForwardCompatible, glfw.True)
|
||||
|
||||
window, err := glfw.CreateWindow(width, height, "Conway's Game of Life", nil, nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
window.MakeContextCurrent()
|
||||
|
||||
return window
|
||||
}
|
||||
|
||||
// initOpenGL initializes OpenGL and returns an intiialized program.
|
||||
func initOpenGL() uint32 {
|
||||
if err := gl.Init(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
version := gl.GoStr(gl.GetString(gl.VERSION))
|
||||
log.Println("OpenGL version", version)
|
||||
|
||||
vertexShader, err := compileShader(vertexShaderSource, gl.VERTEX_SHADER)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fragmentShader, err := compileShader(fragmentShaderSource, gl.FRAGMENT_SHADER)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
prog := gl.CreateProgram()
|
||||
gl.AttachShader(prog, vertexShader)
|
||||
gl.AttachShader(prog, fragmentShader)
|
||||
gl.LinkProgram(prog)
|
||||
return prog
|
||||
}
|
||||
|
||||
// makeVao initializes and returns a vertex array from the points provided.
|
||||
func makeVao(points []float32) uint32 {
|
||||
var vbo uint32
|
||||
gl.GenBuffers(1, &vbo)
|
||||
gl.BindBuffer(gl.ARRAY_BUFFER, vbo)
|
||||
gl.BufferData(gl.ARRAY_BUFFER, 4*len(points), gl.Ptr(points), gl.STATIC_DRAW)
|
||||
|
||||
var vao uint32
|
||||
gl.GenVertexArrays(1, &vao)
|
||||
gl.BindVertexArray(vao)
|
||||
gl.EnableVertexAttribArray(0)
|
||||
gl.BindBuffer(gl.ARRAY_BUFFER, vbo)
|
||||
gl.VertexAttribPointer(0, 3, gl.FLOAT, false, 0, nil)
|
||||
|
||||
return vao
|
||||
}
|
||||
|
||||
func compileShader(source string, shaderType uint32) (uint32, error) {
|
||||
shader := gl.CreateShader(shaderType)
|
||||
|
||||
csources, free := gl.Strs(source)
|
||||
gl.ShaderSource(shader, 1, csources, nil)
|
||||
free()
|
||||
gl.CompileShader(shader)
|
||||
|
||||
var status int32
|
||||
gl.GetShaderiv(shader, gl.COMPILE_STATUS, &status)
|
||||
if status == gl.FALSE {
|
||||
var logLength int32
|
||||
gl.GetShaderiv(shader, gl.INFO_LOG_LENGTH, &logLength)
|
||||
|
||||
log := strings.Repeat("\x00", int(logLength+1))
|
||||
gl.GetShaderInfoLog(shader, logLength, nil, gl.Str(log))
|
||||
|
||||
return 0, fmt.Errorf("failed to compile %v: %v", source, log)
|
||||
}
|
||||
|
||||
return shader, nil
|
||||
}
|
||||
```
|
||||
Let me know if this post was helpful on Twitter [@kylewbanks][20] or down below, and follow me to keep up with future posts!
|
||||
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://kylewbanks.com/blog/tutorial-opengl-with-golang-part-2-drawing-the-game-board
|
||||
|
||||
作者:[kylewbanks ][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/kylewbanks
|
||||
[1]:https://kylewbanks.com/category/golang
|
||||
[2]:https://kylewbanks.com/category/opengl
|
||||
[3]:https://twitter.com/intent/tweet?text=OpenGL%20%26%20Go%20Tutorial%20Part%202%3A%20Drawing%20the%20Game%20Board%20https%3A%2F%2Fkylewbanks.com%2Fblog%2Ftutorial-opengl-with-golang-part-2-drawing-the-game-board%20by%20%40kylewbanks
|
||||
[4]:mailto:?subject=Check%20Out%20%22OpenGL%20%26%20Go%20Tutorial%20Part%202%3A%20Drawing%20the%20Game%20Board%22&body=https%3A%2F%2Fkylewbanks.com%2Fblog%2Ftutorial-opengl-with-golang-part-2-drawing-the-game-board
|
||||
[5]:https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fkylewbanks.com%2Fblog%2Ftutorial-opengl-with-golang-part-2-drawing-the-game-board
|
||||
[6]:https://kylewbanks.com/blog/tutorial-opengl-with-golang-part-1-hello-opengl
|
||||
[7]:https://kylewbanks.com/blog/tutorial-opengl-with-golang-part-2-drawing-the-game-board
|
||||
[8]:https://kylewbanks.com/blog/tutorial-opengl-with-golang-part-3-implementing-the-game
|
||||
[9]:https://github.com/KyleBanks/conways-gol
|
||||
[10]:https://kylewbanks.com/blog/tutorial-opengl-with-golang-part-1-hello-opengl
|
||||
[11]:https://kylewbanks.com/blog/tutorial-opengl-with-golang-part-2-drawing-the-game-board
|
||||
[12]:https://kylewbanks.com/blog/tutorial-opengl-with-golang-part-3-implementing-the-game
|
||||
[13]:https://github.com/KyleBanks/conways-gol
|
||||
[14]:https://twitter.com/kylewbanks
|
||||
[15]:https://kylewbanks.com/blog/tutorial-opengl-with-golang-part-1-hello-opengl
|
||||
[16]:https://kylewbanks.com/blog/tutorial-opengl-with-golang-part-3-implementing-the-game
|
||||
[17]:https://twitter.com/intent/tweet?text=OpenGL%20%26%20Go%20Tutorial%20Part%202%3A%20Drawing%20the%20Game%20Board%20https%3A%2F%2Fkylewbanks.com%2Fblog%2Ftutorial-opengl-with-golang-part-2-drawing-the-game-board%20by%20%40kylewbanks
|
||||
[18]:mailto:?subject=Check%20Out%20%22OpenGL%20%26%20Go%20Tutorial%20Part%202%3A%20Drawing%20the%20Game%20Board%22&body=https%3A%2F%2Fkylewbanks.com%2Fblog%2Ftutorial-opengl-with-golang-part-2-drawing-the-game-board
|
||||
[19]:https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fkylewbanks.com%2Fblog%2Ftutorial-opengl-with-golang-part-2-drawing-the-game-board
|
||||
[20]:https://twitter.com/kylewbanks
|
@ -0,0 +1,597 @@
|
||||
OpenGL & Go Tutorial Part 3: Implementing the Game
|
||||
============================================================
|
||||
|
||||
[Part 1: Hello, OpenGL][8] | [Part 2: Drawing the Game Board][9] | [Part 3: Implementing the Game][10]
|
||||
|
||||
The full source code of the tutorial is available on [GitHub][11].
|
||||
|
||||
Welcome back to the OpenGL & Go Tutorial! If you haven’t gone through [Part 1][12] and [Part 2][13] you’ll definitely want to take a step back and check them out.
|
||||
|
||||
At this point you should have a grid system created and a matrix of cells to represent each unit of the grid. Now it’s time to implement Conway’s Game of Life using the grid as the game board.
|
||||
|
||||
Let’s get started!
|
||||
|
||||
### Implement Conway’s Game
|
||||
|
||||
One of the keys to Conway’s game is that each cell must determine its next state based on the current state of the board, at the same time. This means that if Cell (X=3, Y=4) changes state during its calculation, its neighbor at (X=4, Y=4) must determine its own state based on what (X=3, Y=4) was, not what is has become. Basically, this means we must loop through the cells and determine their next state without modifying their current state before we draw, and then on the next loop of the game we apply the new state and repeat.
|
||||
|
||||
In order to accomplish this, we’ll add two booleans to the cell struct:
|
||||
|
||||
```
|
||||
type cell struct {
|
||||
drawable uint32
|
||||
|
||||
alive bool
|
||||
aliveNext bool
|
||||
|
||||
x int
|
||||
y int
|
||||
}
|
||||
```
|
||||
|
||||
Now let’s add two functions that we’ll use to determine the cell’s state:
|
||||
|
||||
```
|
||||
// checkState determines the state of the cell for the next tick of the game.
|
||||
func (c *cell) checkState(cells [][]*cell) {
|
||||
c.alive = c.aliveNext
|
||||
c.aliveNext = c.alive
|
||||
|
||||
liveCount := c.liveNeighbors(cells)
|
||||
if c.alive {
|
||||
// 1\. Any live cell with fewer than two live neighbours dies, as if caused by underpopulation.
|
||||
if liveCount < 2 {
|
||||
c.aliveNext = false
|
||||
}
|
||||
|
||||
// 2\. Any live cell with two or three live neighbours lives on to the next generation.
|
||||
if liveCount == 2 || liveCount == 3 {
|
||||
c.aliveNext = true
|
||||
}
|
||||
|
||||
// 3\. Any live cell with more than three live neighbours dies, as if by overpopulation.
|
||||
if liveCount > 3 {
|
||||
c.aliveNext = false
|
||||
}
|
||||
} else {
|
||||
// 4\. Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.
|
||||
if liveCount == 3 {
|
||||
c.aliveNext = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// liveNeighbors returns the number of live neighbors for a cell.
|
||||
func (c *cell) liveNeighbors(cells [][]*cell) int {
|
||||
var liveCount int
|
||||
add := func(x, y int) {
|
||||
// If we're at an edge, check the other side of the board.
|
||||
if x == len(cells) {
|
||||
x = 0
|
||||
} else if x == -1 {
|
||||
x = len(cells) - 1
|
||||
}
|
||||
if y == len(cells[x]) {
|
||||
y = 0
|
||||
} else if y == -1 {
|
||||
y = len(cells[x]) - 1
|
||||
}
|
||||
|
||||
if cells[x][y].alive {
|
||||
liveCount++
|
||||
}
|
||||
}
|
||||
|
||||
add(c.x-1, c.y) // To the left
|
||||
add(c.x+1, c.y) // To the right
|
||||
add(c.x, c.y+1) // up
|
||||
add(c.x, c.y-1) // down
|
||||
add(c.x-1, c.y+1) // top-left
|
||||
add(c.x+1, c.y+1) // top-right
|
||||
add(c.x-1, c.y-1) // bottom-left
|
||||
add(c.x+1, c.y-1) // bottom-right
|
||||
|
||||
return liveCount
|
||||
}
|
||||
```
|
||||
|
||||
What’s more interesting is the liveNeighbors function where we return the number of neighbors to the current cell that are in an alivestate. We define an inner function called add that will do some repetitive validation on X and Y coordinates. What it does is check if we’ve passed a number that exceeds the bounds of the board - for example, if cell (X=0, Y=5) wants to check on its neighbor to the left, it has to wrap around to the other side of the board to cell (X=9, Y=5), and likewise for the Y-axis.
|
||||
|
||||
Below the inner add function we call add with each of the cell’s eight neighbors, depicted below:
|
||||
|
||||
```
|
||||
[
|
||||
[-, -, -],
|
||||
[N, N, N],
|
||||
[N, C, N],
|
||||
[N, N, N],
|
||||
[-, -, -]
|
||||
]
|
||||
```
|
||||
|
||||
In this depiction, each cell labeled N is a neighbor to C.
|
||||
|
||||
Now in our main function, where we have our core game loop, let’s call checkState on each cell prior to drawing:
|
||||
|
||||
```
|
||||
func main() {
|
||||
...
|
||||
|
||||
for !window.ShouldClose() {
|
||||
for x := range cells {
|
||||
for _, c := range cells[x] {
|
||||
c.checkState(cells)
|
||||
}
|
||||
}
|
||||
|
||||
draw(cells, window, program)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
func (c *cell) draw() {
|
||||
if !c.alive {
|
||||
return
|
||||
}
|
||||
|
||||
gl.BindVertexArray(c.drawable)
|
||||
gl.DrawArrays(gl.TRIANGLES, 0, int32(len(square)/3))
|
||||
}
|
||||
```
|
||||
|
||||
Let’s fix that. Back in makeCells we’ll use a random number between 0.0 and 1.0 to set the initial state of the game. We’ll define a constant threshold of 0.15 meaning that each cell has a 15% chance of starting in an alive state:
|
||||
|
||||
```
|
||||
import (
|
||||
"math/rand"
|
||||
"time"
|
||||
...
|
||||
)
|
||||
|
||||
const (
|
||||
...
|
||||
|
||||
threshold = 0.15
|
||||
)
|
||||
|
||||
func makeCells() [][]*cell {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
|
||||
cells := make([][]*cell, rows, rows)
|
||||
for x := 0; x < rows; x++ {
|
||||
for y := 0; y < columns; y++ {
|
||||
c := newCell(x, y)
|
||||
|
||||
c.alive = rand.Float64() < threshold
|
||||
c.aliveNext = c.alive
|
||||
|
||||
cells[x] = append(cells[x], c)
|
||||
}
|
||||
}
|
||||
|
||||
return cells
|
||||
}
|
||||
```
|
||||
|
||||
Next in the loop, after creating a cell with the newCell function we set its alive state equal to the result of a random float, between 0.0and 1.0, being less than threshold (0.15). Again, this means each cell has a 15% chance of starting out alive. You can play with this number to increase or decrease the number of living cells at the outset of the game. We also set aliveNext equal to alive, otherwise we’ll get a massive die-off on the first iteration because aliveNext will always be false!
|
||||
|
||||
Now go ahead and give it a run, and you’ll likely see a quick flash of cells that you can’t make heads or tails of. The reason is that your computer is probably way too fast and is running through (or even finishing) the simulation before you have a chance to really see it.
|
||||
|
||||
Let’s reduce the game speed by introducing a frames-per-second limitation in the main loop:
|
||||
|
||||
```
|
||||
const (
|
||||
...
|
||||
|
||||
fps = 2
|
||||
)
|
||||
|
||||
func main() {
|
||||
...
|
||||
|
||||
for !window.ShouldClose() {
|
||||
t := time.Now()
|
||||
|
||||
for x := range cells {
|
||||
for _, c := range cells[x] {
|
||||
c.checkState(cells)
|
||||
}
|
||||
}
|
||||
|
||||
if err := draw(prog, window, cells); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
time.Sleep(time.Second/time.Duration(fps) - time.Since(t))
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now you should be able to see some patterns, albeit very slowly. Increase the FPS to 10 and the size of the grid to 100x100 and you should see some really cool simulations:
|
||||
|
||||
```
|
||||
const (
|
||||
...
|
||||
|
||||
rows = 100
|
||||
columns = 100
|
||||
|
||||
fps = 10
|
||||
|
||||
...
|
||||
)
|
||||
```
|
||||
|
||||
![Conway's Game of Life in OpenGL and Golang Tutorial - Demo Game](https://kylewbanks.com/images/post/golang-opengl-conway-1.gif)
|
||||
|
||||
Try playing with the constants to see how they impact the simulation - cool right? Your very first OpenGL application with Go!
|
||||
|
||||
### What’s Next?
|
||||
|
||||
This concludes the OpenGL with Go Tutorial, but that doesn’t mean you should stop now. Here’s a few challenges to further improve your OpenGL (and Go) knowledge:
|
||||
|
||||
1. Give each cell a unique color.
|
||||
2. Allow the user to specify, via command-line arguments, the grid size, frame rate, seed and threshold. You can see this one implemented on GitHub at [github.com/KyleBanks/conways-gol][4].
|
||||
3. Change the shape of the cells into something more interesting, like a hexagon.
|
||||
4. Use color to indicate the cell’s state - for example, make cells green on the first frame that they’re alive, and make them yellow if they’ve been alive more than three frames.
|
||||
5. Automatically close the window if the simulation completes, meaning all cells are dead or no cells have changed state in the last two frames.
|
||||
6. Move the shader source code out into their own files, rather than having them as string constants in the Go source code.
|
||||
|
||||
### Summary
|
||||
|
||||
Hopefully this tutorial has been helpful in gaining a foundation on OpenGL (and maybe even Go)! It was a lot of fun to make so I can only hope it was fun to go through and learn.
|
||||
|
||||
As I’ve mentioned, OpenGL can be very intimidating, but it’s really not so bad once you get started. You just want to break down your goals into small, achievable steps, and enjoy each victory because while OpenGL isn’t always as tough as it looks, it can certainly be very unforgiving. One thing that I have found helpful when stuck on OpenGL issues was to understand that the way go-gl is generated means you can always use C code as a reference, which is much more popular in tutorials around the internet. The only difference usually between the C and Go code is that functions in Go are prefixed with gl. instead of gl, and constants are prefixed with gl instead of GL_. This vastly increases the pool of knowledge you have to draw from!
|
||||
|
||||
[Part 1: Hello, OpenGL][14] | [Part 2: Drawing the Game Board][15] | [Part 3: Implementing the Game][16]
|
||||
|
||||
The full source code of the tutorial is available on [GitHub][17].
|
||||
|
||||
### Checkpoint
|
||||
|
||||
Here’s the final contents of main.go:
|
||||
|
||||
```
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"math/rand"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-gl/gl/v4.1-core/gl" // OR: github.com/go-gl/gl/v2.1/gl
|
||||
"github.com/go-gl/glfw/v3.2/glfw"
|
||||
)
|
||||
|
||||
const (
|
||||
width = 500
|
||||
height = 500
|
||||
|
||||
vertexShaderSource = `
|
||||
#version 410
|
||||
in vec3 vp;
|
||||
void main() {
|
||||
gl_Position = vec4(vp, 1.0);
|
||||
}
|
||||
` + "\x00"
|
||||
|
||||
fragmentShaderSource = `
|
||||
#version 410
|
||||
out vec4 frag_colour;
|
||||
void main() {
|
||||
frag_colour = vec4(1, 1, 1, 1.0);
|
||||
}
|
||||
` + "\x00"
|
||||
|
||||
rows = 100
|
||||
columns = 100
|
||||
|
||||
threshold = 0.15
|
||||
fps = 10
|
||||
)
|
||||
|
||||
var (
|
||||
square = []float32{
|
||||
-0.5, 0.5, 0,
|
||||
-0.5, -0.5, 0,
|
||||
0.5, -0.5, 0,
|
||||
|
||||
-0.5, 0.5, 0,
|
||||
0.5, 0.5, 0,
|
||||
0.5, -0.5, 0,
|
||||
}
|
||||
)
|
||||
|
||||
type cell struct {
|
||||
drawable uint32
|
||||
|
||||
alive bool
|
||||
aliveNext bool
|
||||
|
||||
x int
|
||||
y int
|
||||
}
|
||||
|
||||
func main() {
|
||||
runtime.LockOSThread()
|
||||
|
||||
window := initGlfw()
|
||||
defer glfw.Terminate()
|
||||
program := initOpenGL()
|
||||
|
||||
cells := makeCells()
|
||||
for !window.ShouldClose() {
|
||||
t := time.Now()
|
||||
|
||||
for x := range cells {
|
||||
for _, c := range cells[x] {
|
||||
c.checkState(cells)
|
||||
}
|
||||
}
|
||||
|
||||
draw(cells, window, program)
|
||||
|
||||
time.Sleep(time.Second/time.Duration(fps) - time.Since(t))
|
||||
}
|
||||
}
|
||||
|
||||
func draw(cells [][]*cell, window *glfw.Window, program uint32) {
|
||||
gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
|
||||
gl.UseProgram(program)
|
||||
|
||||
for x := range cells {
|
||||
for _, c := range cells[x] {
|
||||
c.draw()
|
||||
}
|
||||
}
|
||||
|
||||
glfw.PollEvents()
|
||||
window.SwapBuffers()
|
||||
}
|
||||
|
||||
func makeCells() [][]*cell {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
|
||||
cells := make([][]*cell, rows, rows)
|
||||
for x := 0; x < rows; x++ {
|
||||
for y := 0; y < columns; y++ {
|
||||
c := newCell(x, y)
|
||||
|
||||
c.alive = rand.Float64() < threshold
|
||||
c.aliveNext = c.alive
|
||||
|
||||
cells[x] = append(cells[x], c)
|
||||
}
|
||||
}
|
||||
|
||||
return cells
|
||||
}
|
||||
func newCell(x, y int) *cell {
|
||||
points := make([]float32, len(square), len(square))
|
||||
copy(points, square)
|
||||
|
||||
for i := 0; i < len(points); i++ {
|
||||
var position float32
|
||||
var size float32
|
||||
switch i % 3 {
|
||||
case 0:
|
||||
size = 1.0 / float32(columns)
|
||||
position = float32(x) * size
|
||||
case 1:
|
||||
size = 1.0 / float32(rows)
|
||||
position = float32(y) * size
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
||||
if points[i] < 0 {
|
||||
points[i] = (position * 2) - 1
|
||||
} else {
|
||||
points[i] = ((position + size) * 2) - 1
|
||||
}
|
||||
}
|
||||
|
||||
return &cell{
|
||||
drawable: makeVao(points),
|
||||
|
||||
x: x,
|
||||
y: y,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *cell) draw() {
|
||||
if !c.alive {
|
||||
return
|
||||
}
|
||||
|
||||
gl.BindVertexArray(c.drawable)
|
||||
gl.DrawArrays(gl.TRIANGLES, 0, int32(len(square)/3))
|
||||
}
|
||||
|
||||
// checkState determines the state of the cell for the next tick of the game.
|
||||
func (c *cell) checkState(cells [][]*cell) {
|
||||
c.alive = c.aliveNext
|
||||
c.aliveNext = c.alive
|
||||
|
||||
liveCount := c.liveNeighbors(cells)
|
||||
if c.alive {
|
||||
// 1\. Any live cell with fewer than two live neighbours dies, as if caused by underpopulation.
|
||||
if liveCount < 2 {
|
||||
c.aliveNext = false
|
||||
}
|
||||
|
||||
// 2\. Any live cell with two or three live neighbours lives on to the next generation.
|
||||
if liveCount == 2 || liveCount == 3 {
|
||||
c.aliveNext = true
|
||||
}
|
||||
|
||||
// 3\. Any live cell with more than three live neighbours dies, as if by overpopulation.
|
||||
if liveCount > 3 {
|
||||
c.aliveNext = false
|
||||
}
|
||||
} else {
|
||||
// 4\. Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.
|
||||
if liveCount == 3 {
|
||||
c.aliveNext = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// liveNeighbors returns the number of live neighbors for a cell.
|
||||
func (c *cell) liveNeighbors(cells [][]*cell) int {
|
||||
var liveCount int
|
||||
add := func(x, y int) {
|
||||
// If we're at an edge, check the other side of the board.
|
||||
if x == len(cells) {
|
||||
x = 0
|
||||
} else if x == -1 {
|
||||
x = len(cells) - 1
|
||||
}
|
||||
if y == len(cells[x]) {
|
||||
y = 0
|
||||
} else if y == -1 {
|
||||
y = len(cells[x]) - 1
|
||||
}
|
||||
|
||||
if cells[x][y].alive {
|
||||
liveCount++
|
||||
}
|
||||
}
|
||||
|
||||
add(c.x-1, c.y) // To the left
|
||||
add(c.x+1, c.y) // To the right
|
||||
add(c.x, c.y+1) // up
|
||||
add(c.x, c.y-1) // down
|
||||
add(c.x-1, c.y+1) // top-left
|
||||
add(c.x+1, c.y+1) // top-right
|
||||
add(c.x-1, c.y-1) // bottom-left
|
||||
add(c.x+1, c.y-1) // bottom-right
|
||||
|
||||
return liveCount
|
||||
}
|
||||
|
||||
// initGlfw initializes glfw and returns a Window to use.
|
||||
func initGlfw() *glfw.Window {
|
||||
if err := glfw.Init(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
glfw.WindowHint(glfw.Resizable, glfw.False)
|
||||
glfw.WindowHint(glfw.ContextVersionMajor, 4)
|
||||
glfw.WindowHint(glfw.ContextVersionMinor, 1)
|
||||
glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile)
|
||||
glfw.WindowHint(glfw.OpenGLForwardCompatible, glfw.True)
|
||||
|
||||
window, err := glfw.CreateWindow(width, height, "Conway's Game of Life", nil, nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
window.MakeContextCurrent()
|
||||
|
||||
return window
|
||||
}
|
||||
|
||||
// initOpenGL initializes OpenGL and returns an intiialized program.
|
||||
func initOpenGL() uint32 {
|
||||
if err := gl.Init(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
version := gl.GoStr(gl.GetString(gl.VERSION))
|
||||
log.Println("OpenGL version", version)
|
||||
|
||||
vertexShader, err := compileShader(vertexShaderSource, gl.VERTEX_SHADER)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fragmentShader, err := compileShader(fragmentShaderSource, gl.FRAGMENT_SHADER)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
prog := gl.CreateProgram()
|
||||
gl.AttachShader(prog, vertexShader)
|
||||
gl.AttachShader(prog, fragmentShader)
|
||||
gl.LinkProgram(prog)
|
||||
return prog
|
||||
}
|
||||
|
||||
// makeVao initializes and returns a vertex array from the points provided.
|
||||
func makeVao(points []float32) uint32 {
|
||||
var vbo uint32
|
||||
gl.GenBuffers(1, &vbo)
|
||||
gl.BindBuffer(gl.ARRAY_BUFFER, vbo)
|
||||
gl.BufferData(gl.ARRAY_BUFFER, 4*len(points), gl.Ptr(points), gl.STATIC_DRAW)
|
||||
|
||||
var vao uint32
|
||||
gl.GenVertexArrays(1, &vao)
|
||||
gl.BindVertexArray(vao)
|
||||
gl.EnableVertexAttribArray(0)
|
||||
gl.BindBuffer(gl.ARRAY_BUFFER, vbo)
|
||||
gl.VertexAttribPointer(0, 3, gl.FLOAT, false, 0, nil)
|
||||
|
||||
return vao
|
||||
}
|
||||
|
||||
func compileShader(source string, shaderType uint32) (uint32, error) {
|
||||
shader := gl.CreateShader(shaderType)
|
||||
|
||||
csources, free := gl.Strs(source)
|
||||
gl.ShaderSource(shader, 1, csources, nil)
|
||||
free()
|
||||
gl.CompileShader(shader)
|
||||
|
||||
var status int32
|
||||
gl.GetShaderiv(shader, gl.COMPILE_STATUS, &status)
|
||||
if status == gl.FALSE {
|
||||
var logLength int32
|
||||
gl.GetShaderiv(shader, gl.INFO_LOG_LENGTH, &logLength)
|
||||
|
||||
log := strings.Repeat("\x00", int(logLength+1))
|
||||
gl.GetShaderInfoLog(shader, logLength, nil, gl.Str(log))
|
||||
|
||||
return 0, fmt.Errorf("failed to compile %v: %v", source, log)
|
||||
}
|
||||
|
||||
return shader, nil
|
||||
}
|
||||
```
|
||||
|
||||
Let me know if this post was helpful on Twitter
|
||||
|
||||
[@kylewbanks][18]
|
||||
|
||||
or down below, and follow me to keep up with future posts!
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://kylewbanks.com/blog/tutorial-opengl-with-golang-part-3-implementing-the-game
|
||||
|
||||
作者:[kylewbanks ][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/kylewbanks
|
||||
[1]:https://twitter.com/intent/tweet?text=OpenGL%20%26%20Go%20Tutorial%20Part%203%3A%20Implementing%20the%20Game%20https%3A%2F%2Fkylewbanks.com%2Fblog%2Ftutorial-opengl-with-golang-part-3-implementing-the-game%20by%20%40kylewbanks
|
||||
[2]:mailto:?subject=Check%20Out%20%22OpenGL%20%26%20Go%20Tutorial%20Part%203%3A%20Implementing%20the%20Game%22&body=https%3A%2F%2Fkylewbanks.com%2Fblog%2Ftutorial-opengl-with-golang-part-3-implementing-the-game
|
||||
[3]:https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fkylewbanks.com%2Fblog%2Ftutorial-opengl-with-golang-part-3-implementing-the-game
|
||||
[4]:https://github.com/KyleBanks/conways-gol
|
||||
[5]:https://kylewbanks.com/category/golang
|
||||
[6]:https://kylewbanks.com/category/opengl
|
||||
[7]:https://twitter.com/kylewbanks
|
||||
[8]:https://kylewbanks.com/blog/tutorial-opengl-with-golang-part-1-hello-opengl
|
||||
[9]:https://kylewbanks.com/blog/tutorial-opengl-with-golang-part-2-drawing-the-game-board
|
||||
[10]:https://kylewbanks.com/blog/tutorial-opengl-with-golang-part-3-implementing-the-game
|
||||
[11]:https://github.com/KyleBanks/conways-gol
|
||||
[12]:https://kylewbanks.com/blog/tutorial-opengl-with-golang-part-1-hello-opengl
|
||||
[13]:https://kylewbanks.com/blog/[Part%202:%20Drawing%20the%20Game%20Board](/blog/tutorial-opengl-with-golang-part-2-drawing-the-game-board)
|
||||
[14]:https://kylewbanks.com/blog/tutorial-opengl-with-golang-part-1-hello-opengl
|
||||
[15]:https://kylewbanks.com/blog/tutorial-opengl-with-golang-part-2-drawing-the-game-board
|
||||
[16]:https://kylewbanks.com/blog/tutorial-opengl-with-golang-part-3-implementing-the-game
|
||||
[17]:https://github.com/KyleBanks/conways-gol
|
||||
[18]:https://twitter.com/kylewbanks
|
@ -1,47 +0,0 @@
|
||||
# [The End of the Line for EPEL-5][1]
|
||||
|
||||
|
||||
![](https://cdn.fedoramagazine.org/wp-content/uploads/2017/03/epel5-eol-945x400.png)
|
||||
|
||||
For the last 10 years, the Fedora Project has been building packages for the same release of another operating system. **However on March 31st, 2017, that will come to an end** when Red Hat Enterprise Linux (RHEL) version 5 moves out of production.
|
||||
|
||||
### A Short History of EPEL
|
||||
|
||||
RHEL is a downstream rebuild of a subset of Fedora releases that Red Hat feels it can adequetely support over a multi-year lifetime. While these packages make for a full operating system, there have always been a need by system administrators for ‘more’ packages. Before RHEL-5, many of these packages would be built and supplied by various rebuilders. With Fedora Extras growing to include many of the packages, and several of the rebuilders having joined Fedora, there was an idea of combining forces and creating a dedicated sub-project who would rebuild Fedora packages with specific RHEL releases and then distribute them from Fedora’s centralized servers.
|
||||
|
||||
After much debate and a failure to come up with a catchy name, the Extra Packages for Enterprise Linux (or EPEL) sub-project of Fedora was created. While at first rebuilding packages for RHEL-4, the main goal was to have as many packages available for RHEL-5 when it arrived. It took a lot of hard work getting the plague builders in place, but most of the work was in crafting the rules and guidelines that EPEL would use for the next 10 years. [As anyone can see from the old mail archives][2], the debates were fierce from both various Fedora contributors feeling this took away focus from moving Fedora releases forward to outside contributors worried about conflicts with existing installed packages.
|
||||
|
||||
In the end, EPEL-5 went live sometime in April of 2007 and over the next 10 years grew to a repository of over 5000 source packages and 200,000 unique ip addresses checking in per day at its peak of 240,000 in early 2013\. While every package built for EPEL is done with the RHEL packages, all of these packages have been useful for the various community rebuilds (CentOS, Scientific Linux, Amazon Linux) of RHEL. This meant that growth in those eco-systems brought more users into using EPEL and helping on packaging as later RHEL releases came out. However as these newer releases and rebuilds grew in usage, the number of EPEL-5 users has gradually fallen to around 160,000 unique ip addresses per day. Also over that time, the number of packages supported by developers has fallen and the repository has shrunk in size to 2000 source packages.<>
|
||||
|
||||
Part of the shrinkage was due to the original rules put in place back in 2007\. At that time, Red Hat Enterprise Linux releases were only thought to have an active life time of 6 years before being end of lifed. It was thought that for such a ‘limited’ lifetime, packages could be ‘frozen’ in EPEL like they were in the RHEL release. This meant that whenever possible fixes should be backported and major changes would not be allowed. Time and packaging stands still for no human, and packages would be continually pruned from EPEL-5 as packagers no longer wanted to try and backport fixes. While various rules were loosened to allow for larger changes in packages, the packaging rules that Fedora used have continually moved and improved from 2007\. This has made trying to rebuild a package from newer releases harder and harder with the older operating systems.
|
||||
|
||||
### What Happens on March 31st 2017
|
||||
|
||||
As stated before, on March 31st Red Hat will end of life and no longer put updates out for RHEL-5 for regular customers. This means that
|
||||
Fedora and the various rebuild distributors will start various archive processes. For the EPEL project this means that we will follow the steps that happen every year with Fedora releases.
|
||||
|
||||
1. On **March 27th**, no new builds will be allowed to be pushed for EPEL-5 so that the repository is essentially frozen. This will allow mirrors to have a clear tree of all files.
|
||||
2. All packages in EPEL-5 will be hardlinked on the master mirror from `/pub/epel/5/` and `/pub/epel/testing/5/` to `/pub/archives/epel/`. **This will start happening on the 27th** so all mirrors of archives can populate their disks.
|
||||
3. Because March 31st happens on a Friday, and system administrators do not like Friday surprises, there will be no change then. On **April 3rd**, mirrormanager will be updated to point to the archives.
|
||||
4. On **April 6th**, the /pub/epel/5/ trees will be removed and mirrors will update accordingly.
|
||||
|
||||
For a system administrator who has cron jobs which do yum updates, there should be minimal hassle. The systems will continue to update and even install any packages which were in the archives at that time. There will be breakage for system administrators who have scripts which directly download files from mirrors. Those scripts will need to change to the new canonical location of /pub/archive/epel/5/.
|
||||
|
||||
While irksome, this is a blessing in disguise for many system administrators who will still be using an older Linux. Because packages have been regularly removed from EPEL-5, the various support mailing lists and irc channels have regular requests from system administrators wondering where some package they needed has gone. After the archive is done, this won’t be a problem because no more packages will be removed :).
|
||||
|
||||
For system administrators who have been hit by this problem, the older EPEL packages are still available though in a much slower method. All EPEL packages are built in the Fedora Koji system, and so older builds of packages can be found using [Koji search.][3]
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://fedoramagazine.org/the-end-of-the-line-for-epel-5/
|
||||
|
||||
作者:[smooge][a]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:http://smooge.id.fedoraproject.org/
|
||||
[1]:https://fedoramagazine.org/the-end-of-the-line-for-epel-5/
|
||||
[2]:https://www.redhat.com/archives/epel-devel-list/2007-March/thread.html
|
||||
[3]:https://koji.fedoraproject.org/koji/search
|
62
sources/tech/20170320 Why Go.md
Normal file
62
sources/tech/20170320 Why Go.md
Normal file
@ -0,0 +1,62 @@
|
||||
Why Go?
|
||||
============================================================
|
||||
|
||||
A few weeks ago I was asked by a friend, “why should I care about Go”? They knew that I was passionate about Go, but wanted to know why I thought _other people_ should care. This article contains three salient reasons why I think Go is an important programming language.
|
||||
|
||||
# Safety
|
||||
|
||||
As individuals, you and I may be perfectly capable of writing a program in C that neither leaks memory or reuses it unsafely. However, with more than [40 years][5] of experience, it is clear that collectively, programmers working in C are unable to reliably do so _en masse_ .
|
||||
|
||||
Despite static code analysis, valgrind, tsan, and `-Werror` being available for a decades, there is scant evidence those tools have achieved widespread acknowledgement, let alone widespread adoption. In aggregate, programmers have shown they simply cannot safely manage their own memory. It’s time to move away from C.
|
||||
|
||||
Go does not rely on the programmer to manage memory directly, instead all memory allocation is managed by the language runtime, initialized before use, and bounds checked when necessary. It’s certainly not the first mainstream language that offered these safety guarantees, Java (1995) is probably a contender for that crown. The point being, the world has no appetite for unsafe programming languages, thus Go is memory safe by default.
|
||||
|
||||
# Developer productivity
|
||||
|
||||
The point at which developer time became more expensive than hardware time was crossed back in the late 1970s. Developer productivity is a sprawling topic but it boils down to this; how much time do you spend doing useful work vs waiting for the compiler or hopelessly lost in a foreign codebase.
|
||||
|
||||
The joke goes that Go was developed while waiting for a [C++ program to compile][6]. Fast compilation is a key feature of Go and a key recruiting tool to attract new developers. While compilation speed remains a [constant battleground][7], it is fair to say that compilations which take minutes in other languages, take seconds in Go.
|
||||
|
||||
More fundamental to the question of developer productivity, Go programmers realise that code is _written to be read_ and so place the [act of reading code above the act of writing it][8]. Go goes so far as to enforce, via tooling and custom, that all code by formatted in a specific style. This removes the friction of learning a project specific language sub-dialect and helps spot mistakes because they just _look incorrect._
|
||||
|
||||
Due to a focus on analysis and mechanical assistance, a growing set of tools that exist to spot common coding errors have been adopted by Go developers in a way that never struck a chord with C programmers—Go developers _want_ tools to help them keep their code clean.
|
||||
|
||||
# Concurrency
|
||||
|
||||
For more than a decade, chip designers have been warning that the [free lunch is over][9]. Hardware parallelism, from the lowliest mobile phone to the most power hungry server, in the form of [more, slower, cpu cores][10], is only available _if_ your language can utilise them. Therefore, concurrency needs to be built into the software we write to run on today’s hardware.
|
||||
|
||||
Go takes a step beyond languages that expose the operating system’s multi-process or multi-threading parallelism models by offering a [lightweight concurrency model based on coroutines][11], or goroutines as they are known in Go. Goroutines allows the programmer to eschew convoluted callback styles while the language runtime makes sure that there will be just enough threads to keep your cores active.
|
||||
|
||||
# The rule of three
|
||||
|
||||
These were my three reasons for recommending Go to my friend; safety, productivity, and concurrency. Individually, there are languages that cover one, possibly two of these domains, but it is the combination of all three that makes Go an excellent choice for mainstream programmers today.
|
||||
|
||||
### Related Posts:
|
||||
|
||||
1. [Why Go and Rust are not competitors][1]
|
||||
2. [Hear me speak about Go performance at OSCON][2]
|
||||
3. [I’m speaking at GopherChina and GopherCon Singapore][3]
|
||||
4. [Stress test your Go packages][4]
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://dave.cheney.net/2017/03/20/why-go
|
||||
|
||||
作者:[Dave Cheney][a]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:https://dave.cheney.net/
|
||||
[1]:https://dave.cheney.net/2015/07/02/why-go-and-rust-are-not-competitors
|
||||
[2]:https://dave.cheney.net/2015/05/31/hear-me-speak-about-go-performance-at-oscon
|
||||
[3]:https://dave.cheney.net/2017/02/09/im-speaking-at-gopherchina-and-gophercon-singapore
|
||||
[4]:https://dave.cheney.net/2013/06/19/stress-test-your-go-packages
|
||||
[5]:https://en.wikipedia.org/wiki/C_(programming_language)
|
||||
[6]:https://commandcenter.blogspot.com.au/2012/06/less-is-exponentially-more.html
|
||||
[7]:https://dave.cheney.net/2016/11/19/go-1-8-toolchain-improvements
|
||||
[8]:https://twitter.com/rob_pike/status/791326139012620288
|
||||
[9]:http://www.gotw.ca/publications/concurrency-ddj.htm
|
||||
[10]:https://www.technologyreview.com/s/601441/moores-law-is-dead-now-what/
|
||||
[11]:https://blog.golang.org/concurrency-is-not-parallelism
|
456
sources/tech/20170322 From Node to Go A High-Level Comparison.md
Normal file
456
sources/tech/20170322 From Node to Go A High-Level Comparison.md
Normal file
@ -0,0 +1,456 @@
|
||||
From Node to Go: A High-Level Comparison
|
||||
============================================================
|
||||
|
||||
At XO Group, we primarily work with Node and Ruby to build out our system of interconnected services. We get the implicit performance benefits of Node mixed with the access to a large, established repository of packages. We also have the ability to easily break out plugins and modules that can be published and reused across the company. This greatly increases developer efficiency and allows us to make scalable and reliable applications in a short amount of time. Furthermore, the large Node community makes it easy for our engineers to contribute open source software (see [BunnyBus][9] or [Felicity][10]).
|
||||
|
||||
Although a good portion of my college days and early career was spent using strict compiled languages, like C++ and C#, I eventually shifted to using Javascript. While I love the freedom and flexibility, I recently found myself nostalgic for static and structured languages. That’s when a coworker turned my attention to Go.
|
||||
|
||||
Coming from Javascript, there are some similarities between the two languages. Both are very fast, fairly easy to learn, have an expressive syntax, and a niche in the development community. There isn’t a perfect programming language and you should always choose a language that fits the project at hand; In this post, I will attempt to illustrate some of the key differences between the two languages at a high level and hopefully encourage anyone new to Go to give it a ̶g̶o̶ chance.
|
||||
|
||||
|
||||
* * *
|
||||
|
||||
### General Differences
|
||||
|
||||
Before we dive into specifics, we should understand some important distinctions between the two languages.
|
||||
|
||||
Go, or Golang, is a free, open-source programming language created by Google in 2007\. It was designed to be fast and simple. Go is compiled down into machine code, which is where its speed is derived from. Debugging is fairly easy with a compiled language because you are able to catch a large chunk of errors early on. It is also a strongly typed language which helps with data integrity and finding type errors at compile time.
|
||||
|
||||
Javascript, on the other hand, is a loosely-typed language. Aside from the added burden of data validation and “truthy” evaluation pitfalls, using a loosely-typed language can can have its own benefits. There is no need for interfaces or generics and currying/flexible arity make functions extremely versatile. Javascript is interpreted at runtime, which can lead to issues with error handling and debugging. Node is a Javascript runtime built on Google’s V8 virtual machine making it a lightweight and fast platform for web development.
|
||||
|
||||
* * *
|
||||
|
||||
### Syntax
|
||||
|
||||
Coming from Javascript, Go’s simple and intuitive syntax was very inviting. Since both languages’ syntaxes are said to have evolved from C, there is quite a bit of overlap. Go is commonly referred to as an ‘easy language to learn.’ This is due to the developer-friendly tools, pared-down syntax, and opinionated conventions.
|
||||
|
||||
Go has a number of built-in features that make development a bit easier. The standard Go build tool lets you compile your code down into a binary file or executable with the go build command. Running tests with the built-in test suite are as simple as calling go test. Things like natively-supported concurrency are even available at the language level.
|
||||
|
||||
According to the [Go developers at Google][11], programming today is too complicated with too much “book keeping, repetition, and clerical work.” This is why the Go’s syntax was designed to be clean and simple in order to reduce clutter, increase efficiency, and improve readability. It also encourages developers to write explicit, easy to understand code. As a result, Go only has [25 unique keywords][12] and one type of loop (for-loop) as opposed to [~84 keywords][13] (reserved words, objects, properties, and methods) in Javascript.
|
||||
|
||||
In order to illustrate some syntactical differences and similarities, let’s look at a couple of examples:
|
||||
|
||||
* Punctuation: Go strips out any superfluous punctuation in order to increase efficiency and readability. Although Javascript’s use of punctuation is somewhat minimal (see: [Lisp][1]) and often optional, I definitely enjoy the simplicity with Go.
|
||||
|
||||
```
|
||||
// Javascript with parentheses and semicolons
|
||||
for (var i = 0; i < 10; i++) {
|
||||
console.log(i);
|
||||
}
|
||||
```
|
||||
|
||||
Punctuation in Javascript
|
||||
|
||||
```
|
||||
// Go uses minimal punctuation
|
||||
for i := 0; i < 10; i++ {
|
||||
fmt.Println(i)
|
||||
}
|
||||
```
|
||||
Punctuation in Go
|
||||
|
||||
* Assignment: Since Go is strongly typed, you have access type inference on initialization with the := operator to reduce [stuttering][2], whereas Javascript declares types on runtime.
|
||||
|
||||
|
||||
```
|
||||
// Javascript assignment
|
||||
var foo = "bar";
|
||||
```
|
||||
|
||||
Assignment in Javascript
|
||||
|
||||
```
|
||||
// Go assignment
|
||||
var foo string //without type derivation
|
||||
foo = "bar"
|
||||
|
||||
foo := "bar" //with type derivation
|
||||
```
|
||||
|
||||
Assignment in Go</figcaption>
|
||||
|
||||
* Exporting: In Javascript, you must explicitly export from a module. In Go, any capitalized functions will be exported.
|
||||
|
||||
|
||||
```
|
||||
const Bar = () => {};
|
||||
|
||||
module.exports = {
|
||||
Bar
|
||||
}
|
||||
```
|
||||
|
||||
Exporting in Javascript
|
||||
|
||||
```
|
||||
// Go export
|
||||
package foo //define package name
|
||||
func Bar (s string) string {
|
||||
//Bar will be exported
|
||||
}
|
||||
```
|
||||
|
||||
Exporting in Go
|
||||
|
||||
* Importing: The _required_ library is necessary for importing dependencies/modules in Javascript, whereas Go utilizes the native import keyword with the import path to the package. Another distinction is that, unlike Node’s central NPM repository for packages, Go uses URLs for the import path on non-standard libraries in order to directly clone dependencies from their origin. Although this provides a simple and intuitive dependency management, versioning packages can be a bit more tedious in Go as opposed to updating the _package.json_ file in Node.
|
||||
```
|
||||
// Javascript import
|
||||
var foo = require('foo');
|
||||
foo.bar();
|
||||
```
|
||||
|
||||
Importing in Javascript
|
||||
|
||||
|
||||
```
|
||||
// Go import
|
||||
import (
|
||||
"fmt" // part of Go’s standard library
|
||||
"github.com/foo/foo" // imported directly from repository
|
||||
)
|
||||
foo.Bar()
|
||||
```
|
||||
|
||||
Importing in Go
|
||||
|
||||
* Returns: Go’s multiple value returns allow for elegantly passing and handling values and errors, as well as reducing the improper passing of values by reference. In Javascript, multiple values must be returned by an array or object.
|
||||
|
||||
```
|
||||
// Javascript - return multiple values
|
||||
function foo() {
|
||||
return {a: 1, b: 2};
|
||||
}
|
||||
const { a, b } = foo();
|
||||
```
|
||||
|
||||
Returns in Javascript
|
||||
|
||||
```
|
||||
// Go - return multiple values
|
||||
func foo() (int, int) {
|
||||
return 1, 2
|
||||
}
|
||||
a, b := foo()
|
||||
```
|
||||
|
||||
Returns in Go
|
||||
|
||||
|
||||
* Errors: Go encourages catching errors often and where they occur as opposed to bubbling the error up in a callback in Node.
|
||||
|
||||
```
|
||||
// Node error handling
|
||||
foo('bar', function(err, data) {
|
||||
//handle error
|
||||
}
|
||||
```
|
||||
|
||||
Errors in Javascript
|
||||
|
||||
|
||||
```
|
||||
//Go error handling
|
||||
foo, err := bar()
|
||||
if err != nil {
|
||||
// handle error with defer, panic, recover, or log.fatal, etc...
|
||||
}
|
||||
```
|
||||
|
||||
Errors in Go
|
||||
|
||||
* Variadic Functions: Both Go and Javascript support functions that accept a fluid number of arguments.
|
||||
|
||||
```
|
||||
function foo (...args) {
|
||||
console.log(args.length);
|
||||
}
|
||||
|
||||
foo(); // 0
|
||||
foo(1, 2, 3); // 3
|
||||
```
|
||||
|
||||
Variadic Function in Javascript
|
||||
|
||||
```
|
||||
func foo (args ...int) {
|
||||
fmt.Println(len(args))
|
||||
}
|
||||
|
||||
func main() {
|
||||
foo() // 0
|
||||
foo(1,2,3) // 3
|
||||
}
|
||||
```
|
||||
|
||||
Variadic Function in Go
|
||||
|
||||
|
||||
* * *
|
||||
|
||||
### Communities
|
||||
|
||||
Although Go and Node have their differences when it comes to which programming paradigms they enable to be easier, they both have unique and supportive followings. One area where Node outshines Go is in the sheer size of their package library and community. Node package manager (NPM), the largest package registry in the world, has over [410,000 packages growing at an alarming rate of 555 new packages per day][14]. That number may seem staggering (and it is), however, something to keep in mind is that many of these packages are redundant and/or non-production quality. In contrast, Go has about 130,000 packages.
|
||||
|
||||
![](https://cdn-images-1.medium.com/max/800/0*0oUnVVKxuUrvVG3F.)
|
||||
|
||||
Module Counts for Node and Go
|
||||
|
||||
Although Node and Go are around the same age, Javascript is more widely used — boasting a large development and open-source community. This is of course because Node was developed for the general public with a robust package manager from the start while Go was specifically built for Google. [The Spectrum ratings][15] below show the top web development languages based on current trends.
|
||||
|
||||
![](https://cdn-images-1.medium.com/max/800/0*o2SmnUo67xeaFbYZ.)
|
||||
|
||||
Spectrum Ratings for top 7 web development programming languages
|
||||
|
||||
While Javascript’s popularity seems to have stayed relatively static over recent years, [Go has been trending up][16].
|
||||
|
||||
|
||||
![](https://cdn-images-1.medium.com/max/800/0*zX5Yg3whLczpSif_.)
|
||||
|
||||
Programming language trends
|
||||
|
||||
* * *
|
||||
|
||||
### Performance
|
||||
|
||||
What if your primary concern is speed? In this day and age, it seems performance optimizations are more important than ever. People don’t like to wait for information. In fact, [40% of users will abandon your site if it takes longer than 3 seconds to load][17].
|
||||
|
||||
Node is often touted as a highly performant because of it’s non-blocking asynchronous I/O. Also, as I mentioned before, Node is run on Google’s V8 engine which was optimized for dynamic languages. Go on the other hand was designed with speed in mind. [The developers at Google][18] achieved this by building “an expressive but lightweight type system; concurrency and garbage collection; rigid dependency specification; and so on.”
|
||||
|
||||
To compare the performance of Node and Go, I ran a couple of tests. These focus on the rudimentary, low-level abilities of the languages. If I had been testing something like HTTP requests or time-intensive processes, I would have used Go’s language-level concurrency tools (goroutines/channels). Instead, I stuck to basic features of each language (see [Concurrency in Three Flavors][19] for a deeper look into goroutines and channels).
|
||||
|
||||
I also included Python in the benchmarks so we feel good about the Node and Go results no matter what.
|
||||
|
||||
#### Loop/Arithmetic
|
||||
|
||||
Iterating through a billion items and adding them up:
|
||||
|
||||
```
|
||||
var r = 0;
|
||||
for (var c = 0; c < 1000000000; c++) {
|
||||
r += c;
|
||||
}
|
||||
```
|
||||
|
||||
Node
|
||||
|
||||
```
|
||||
package main
|
||||
func main() {
|
||||
var r int
|
||||
for c := 0; c < 1000000000; c++ {
|
||||
r += c
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
go
|
||||
|
||||
|
||||
```
|
||||
sum(xrange(1000000000))
|
||||
```
|
||||
|
||||
Python
|
||||
|
||||
![](https://cdn-images-1.medium.com/max/800/1*5u0Q9nmpkcsp2ltzmLO6CQ.png)
|
||||
|
||||
Results
|
||||
|
||||
The clear loser here is Python clocking in at over 7 seconds. On the other hand, both Node and Go were extremely efficient, clocking in at 900 ms and 408 ms, respectively.
|
||||
|
||||
_Edit: As some of the comments suggest, Python’s performance could be improved. The results have been updated to reflect those changes. Also, the use of PyPy greatly improves the performance. When run using Python 3.6.1 and PyPy 3.5.7, the performance improves to 1.234 seconds, but still falls short of Go and Node._
|
||||
|
||||
#### I/O
|
||||
|
||||
Iterating over 1 million numbers and writing them to a file:
|
||||
|
||||
|
||||
```
|
||||
var fs = require('fs');
|
||||
var wstream = fs.createWriteStream('node');
|
||||
|
||||
for (var c = 0; c < 1000000; ++c) {
|
||||
wstream.write(c.toString());
|
||||
}
|
||||
wstream.end();
|
||||
```
|
||||
|
||||
Node
|
||||
|
||||
```
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func main() {
|
||||
file, _ := os.Create("go")
|
||||
for c := 0; c < 1000000; c++ {
|
||||
num := strconv.Itoa(c)
|
||||
io.WriteString(file, num)
|
||||
}
|
||||
file.Close()
|
||||
}
|
||||
```
|
||||
|
||||
go
|
||||
|
||||
```
|
||||
with open("python", "a") as text_file:
|
||||
for i in range(1000000):
|
||||
text_file.write(str(i))
|
||||
```
|
||||
|
||||
Python
|
||||
|
||||
![](https://cdn-images-1.medium.com/max/800/1*NbASStIZewj4vh1RYxXqEQ.png)
|
||||
|
||||
Results
|
||||
|
||||
Once again, Python is third at 7.94 seconds. The gap between Node and Go is small in this test, with Node taking about 1.164 seconds and Go taking 1.477 seconds (although this includes the time it takes for the Go code to compile via go run — the compiled binary shaves off another ~200 ms).
|
||||
|
||||
#### Bubble Sort
|
||||
|
||||
Iterating 10 million times over a 10-item array and sorting:
|
||||
|
||||
```
|
||||
const toBeSorted = [1, 3, 2, 4, 8, 6, 7, 2, 3, 0];
|
||||
|
||||
function bubbleSort(input) {
|
||||
var n = input.length;
|
||||
var swapped = true;
|
||||
while (swapped) {
|
||||
swapped = false;
|
||||
for (var i = 0; i < n; i++) {
|
||||
if (input[i - 1] > input [i]) {
|
||||
[input[i], input[i - 1]] = [input[i - 1], input[i]];
|
||||
swapped = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var c = 0; c < 10000000; c++) {
|
||||
bubbleSort(toBeSorted);
|
||||
}
|
||||
```
|
||||
|
||||
Node
|
||||
|
||||
```
|
||||
package main
|
||||
|
||||
var toBeSorted [10]int = [10]int{1, 3, 2, 4, 8, 6, 7, 2, 3, 0}
|
||||
|
||||
func bubbleSort(input [10]int) {
|
||||
n := len(input)
|
||||
swapped := true
|
||||
for swapped {
|
||||
swapped = false
|
||||
for i := 1; i < n; i++ {
|
||||
if input[i-1] > input[i] {
|
||||
input[i], input[i-1] = input[i-1], input[i]
|
||||
swapped = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
for c := 0; c < 10000000; c++ {
|
||||
bubbleSort(toBeSorted)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
go
|
||||
|
||||
|
||||
```
|
||||
toBeSorted = [1, 3, 2, 4, 8, 6, 7, 2, 3, 0]
|
||||
|
||||
def bubbleSort(input):
|
||||
length = len(input)
|
||||
swapped = True
|
||||
|
||||
while swapped:
|
||||
swapped = False
|
||||
for i in range(1,length):
|
||||
if input[i - 1] > input[i]:
|
||||
input[i], input[i - 1] = input[i - 1], input[i]
|
||||
swapped = True
|
||||
|
||||
for i in range(10000000):
|
||||
bubbleSort(toBeSorted)
|
||||
```
|
||||
Python
|
||||
|
||||
|
||||
![](https://cdn-images-1.medium.com/max/800/0*k9xE1bfJDWz4P19g.)
|
||||
|
||||
<figcaption class="imageCaption" style="position: relative; left: 0px; width: 700px; top: 0px; margin-top: 10px; color: rgba(0, 0, 0, 0.6); outline: 0px; text-align: center; z-index: 300; --baseline-multiplier:0.157; font-family: medium-content-sans-serif-font, "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Geneva, Arial, sans-serif; font-feature-settings: 'liga' 1, 'lnum' 1; font-size: 14px; line-height: 1.4; letter-spacing: 0px;">Results</figcaption>
|
||||
|
||||
As usual, Python’s performance was the poorest, completing the task at hand in about 13 seconds. Go was able to finish the task over two times faster than Node.
|
||||
|
||||
#### Verdict
|
||||
|
||||
Go is the clear winner in all three tests, but Node, for the most part, performs admirably. And Python was there, too. To be clear, performance isn’t everything when choosing a programming language. If your application doesn’t need to process high amounts data, then the differences in performance between Node and Go may be negligible. For some additional comparisons on performance, see the following:
|
||||
|
||||
* [Node Vs. Go][3]
|
||||
* [Multiple Language Performance Test][4]
|
||||
* [Benchmarks Game][5]
|
||||
|
||||
|
||||
* * *
|
||||
|
||||
### Conclusion
|
||||
|
||||
This post is not to prove that one language is better than another. Every programming language has its place in the software development community for one reason or another. My intentions were to highlight the differences between Go and Node, as well as promote exposure to a new web development language. When choosing a language for a given project, there are a lot of different factors to consider including developer familiarity, cost, and practicality. I encourage a thorough low-level analysis when deciding what language is right for you.
|
||||
|
||||
As we have seen, there are several benefits to Go. The raw performance, simple syntax, and relatively shallow learning curve make it ideal for scalable and secure web applications. With it’s fast growth in adoption and community involvement, there is no reason Go can’t become a prominent player in modern web development. That being said, I believe that Node is moving in the right direction to remain a powerful and useful language if implemented correctly. It has a large following and active community that makes it a simple platform for getting a web application up and running in no time.
|
||||
|
||||
* * *
|
||||
|
||||
### Resources
|
||||
|
||||
If you are interested in learning more about Go, consider the following resources:
|
||||
|
||||
* [Golang Website][6]
|
||||
* [Golang Wiki][7]
|
||||
* [Golang Subreddit][8]
|
||||
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://medium.com/xo-tech/from-node-to-go-a-high-level-comparison-56c8b717324a#.byltlz535
|
||||
|
||||
作者:[John Stamatakos][a]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:https://medium.com/@johnstamatakos?source=post_header_lockup
|
||||
[1]:https://en.wikipedia.org/wiki/Lisp_%28programming_language%29
|
||||
[2]:https://golang.org/doc/faq#principles
|
||||
[3]:https://jaxbot.me/articles/node-vs-go-2014
|
||||
[4]:https://hashnode.com/post/comparison-nodejs-php-c-go-python-and-ruby-cio352ydg000ym253frmfnt70
|
||||
[5]:https://benchmarksgame.alioth.debian.org/u64q/compare.php?lang=go&lang2=node
|
||||
[6]:https://golang.org/doc/#learning
|
||||
[7]:https://github.com/golang/go/wiki/Learn
|
||||
[8]:https://www.reddit.com/r/golang/
|
||||
[9]:https://medium.com/xo-tech/bunnybus-building-a-data-transit-system-b9647f6283e5#.l64fdvfys
|
||||
[10]:https://medium.com/xo-tech/introducing-felicity-7b6d0b734ce#.hmloiiyx8
|
||||
[11]:https://golang.org/doc/faq
|
||||
[12]:https://golang.org/ref/spec#Keywords
|
||||
[13]:https://www.w3schools.com/js/js_reserved.asp
|
||||
[14]:http://www.modulecounts.com/
|
||||
[15]:http://spectrum.ieee.org/static/interactive-the-top-programming-languages-2016
|
||||
[16]:http://www.tiobe.com/tiobe-index/
|
||||
[17]:https://hostingfacts.com/internet-facts-stats-2016/
|
||||
[18]:https://golang.org/doc/faq
|
||||
[19]:https://medium.com/xo-tech/concurrency-in-three-flavors-51ed709876fb#.khvqrttxa
|
@ -0,0 +1,277 @@
|
||||
How to Install iRedMail on CentOS 7 for Samba4 AD Integration – Part 10
|
||||
============================================================
|
||||
|
||||
This series of tutorials will guide you on how to integrate iRedMail installed on a CentOS 7 machine with a [Samba4 Active Directory Domain Controller][3] in order for domain accounts to send or receive mail via Thunderbird desktop client or via Roundcube web interface.
|
||||
|
||||
The CentOS 7 server where iRedMail will be installed will allow SMTP or mail routing services via ports 25 and 587 and will also serve as a mail delivery agent through Dovecot, providing POP3 and IMAP services, both secured with self-signed certificates issued on the installation process.
|
||||
|
||||
The recipient mailboxes will be stored on the same CentOS server along with the webmail user agent provided by Roundcube. Samba4 Active Directory will be used by iRedMail to query and authenticate recipient accounts against the realm, to create mail lists with the help of Active Directory groups and to control the mail accounts via Samba4 AD DC.
|
||||
|
||||
#### Requirements:
|
||||
|
||||
1. [Create an Active Directory Infrastructure with Samba4 on Ubuntu][1]
|
||||
|
||||
### Step 1: Install iRedMail in CentOS 7
|
||||
|
||||
1. Before starting with iRedMail installation first make sure you have a fresh CentOS 7 operating system installed on your machine using the instructions provided by this guide:
|
||||
|
||||
1. [Fresh Installation of CentOS 7 Minimal][2]
|
||||
|
||||
2. Also, assure that the system is up-to-date with the latest security and packages updates by issuing the below command.
|
||||
|
||||
```
|
||||
# yum update
|
||||
```
|
||||
|
||||
3. The system will also need a FQDN hostname set by issuing the below command. Replace `mail.tecmint.lan` variable with your own custom FQDN.
|
||||
|
||||
```
|
||||
# hostnamectl set-hostname mail.tecmint.lan
|
||||
```
|
||||
|
||||
Verify system hostname with the below commands.
|
||||
|
||||
```
|
||||
# hostname -s # Short name
|
||||
# hostname -f # FQDN
|
||||
# hostname -d # Domain
|
||||
# cat /etc/hostname # Verify it with cat command
|
||||
```
|
||||
[
|
||||
![Verify CentOS 7 Hostname](http://www.tecmint.com/wp-content/uploads/2017/03/Verify-CentOS-7-Hostname.png)
|
||||
][4]
|
||||
|
||||
Verify CentOS 7 Hostname
|
||||
|
||||
4. Map the machine FQDN and short name against the machine loopback IP address by manually editing `/etc/hosts` file. Add the values as illustrated below and replace `mail.tecmint.lan` and mail values accordingly.
|
||||
|
||||
```
|
||||
127.0.0.1 mail.tecmint.lan mail localhost localhost.localdomain
|
||||
```
|
||||
|
||||
5. iRedMail technicians recommends that SELinux should be completely disabled. Disable SELinux by editing /etc/selinux/config file and set SELINUX parameter from `permissive` to `disabled` as illustrated below.
|
||||
|
||||
```
|
||||
SELINUX=disabled
|
||||
```
|
||||
|
||||
Reboot the machine to apply new SELinux policies or run setenforce with 0 parameter to force SELinux to instantly disable.
|
||||
|
||||
```
|
||||
# reboot
|
||||
OR
|
||||
# setenforce 0
|
||||
```
|
||||
|
||||
6. Next, install the following packages that will come in-handy later for system administration:
|
||||
|
||||
```
|
||||
# yum install bzip2 net-tools bash-completion wget
|
||||
```
|
||||
|
||||
7. In order to install iRedMail, first go to the download page [http://www.iredmail.org/download.html][5] and grab the latest archive version of the software by issuing the below command.
|
||||
|
||||
```
|
||||
# wget https://bitbucket.org/zhb/iredmail/downloads/iRedMail-0.9.6.tar.bz2
|
||||
```
|
||||
|
||||
8. After the download finishes, extract the compressed archive and enter the extracted iRedMail directory by issuing the following commands.
|
||||
|
||||
```
|
||||
# tar xjf iRedMail-0.9.6.tar.bz2
|
||||
# cd iRedMail-0.9.6/
|
||||
# ls
|
||||
```
|
||||
|
||||
9. Start the installation process by executing iRedMail shell script with the following command. From now on a series of questions will be asked by the installer.
|
||||
|
||||
```
|
||||
# bash iRedMail.sh
|
||||
```
|
||||
|
||||
10. On the first welcome prompt hit on `Yes` to proceed further with the installation.
|
||||
|
||||
[
|
||||
![iRedMail Setup Wizard](http://www.tecmint.com/wp-content/uploads/2017/03/iRedMail-Setup-Wizard.png)
|
||||
][6]
|
||||
|
||||
iRedMail Setup Wizard
|
||||
|
||||
11. Next, choose the location where all the mail will be stored. The default directory that iRedMail uses to store mailboxes is `/var/vmail/` system path.
|
||||
|
||||
If this directory is located under a partition with enough storage to host mail for all your domain accounts then hit on Next to continue.
|
||||
|
||||
Otherwise change the default location with a different directory in case if you’ve configured a larger partition dedicated to mail storage.
|
||||
|
||||
[
|
||||
![iRedMail Mail Storage Path](http://www.tecmint.com/wp-content/uploads/2017/03/iRedMail-Mail-Storage-Path.png)
|
||||
][7]
|
||||
|
||||
iRedMail Mail Storage Path
|
||||
|
||||
12. On the next step choose the frontend web server through which you will interact with iRedMail. iRedMail administration panel will be completely disabled later, so we will use the frontend web server only to access accounts mail via Roundcube web panel.
|
||||
|
||||
If you don’t have thousands of mail accounts per hour accessing the webmail interface you should go with Apache web server do to its flexibility and easy management.
|
||||
|
||||
[
|
||||
![iRedMail Preferred Web Server](http://www.tecmint.com/wp-content/uploads/2017/03/iRedMail-Preferred-Web-Server.png)
|
||||
][8]
|
||||
|
||||
iRedMail Preferred Web Server
|
||||
|
||||
13. On this step choose OpenLDAP backend database for compatibility reasons with Samba4 domain controller and hit Next to continue, although we won’t use this OpenLDAP database later once we’ll integrate iRedMail to Samba domain controller.
|
||||
|
||||
[
|
||||
![iRedMail LDAP Backend](http://www.tecmint.com/wp-content/uploads/2017/03/iRedMail-LDAP-Backend.png)
|
||||
][9]
|
||||
|
||||
iRedMail LDAP Backend
|
||||
|
||||
14. Next, specify your Samba4 domain name for LDAP suffix as illustrated on the image below and hit Next to continue.
|
||||
|
||||
[
|
||||
![iRedMail LDAP Suffix](http://www.tecmint.com/wp-content/uploads/2017/03/iRedMail-LDAP-Suffix.png)
|
||||
][10]
|
||||
|
||||
iRedMail LDAP Suffix
|
||||
|
||||
15. On the next prompt enter your domain name only and hit Next to move on. Replace `tecmint.lan` value accordingly.
|
||||
|
||||
[
|
||||
![iRedMail Mail Domain](http://www.tecmint.com/wp-content/uploads/2017/03/iRedMail-Mail-Domain.png)
|
||||
][11]
|
||||
|
||||
iRedMail Mail Domain
|
||||
|
||||
16. Now, setup a password for `postmaster@yourdomain.tld` administrator and hit Next to continue.
|
||||
|
||||
[
|
||||
![iRedMail Mail Domain Administrator](http://www.tecmint.com/wp-content/uploads/2017/03/iRedMail-Mail-Domain-Administrator.png)
|
||||
][12]
|
||||
|
||||
iRedMail Mail Domain Administrator
|
||||
|
||||
17. Next, choose from the list the optional components you want to integrate with your mail server. I strongly recommend to install Roundcube in order to provide a web interface for domain accounts to access mail, although Roundcube can be installed and configured on a different machine for this task in order to free mail server resources in case of high loads.
|
||||
|
||||
For local domains with restricted internet access and especially while we’re using domain integration the other components are not very useful, except Awstats in case you need mail analysis.
|
||||
|
||||
[
|
||||
![iRedMail Optional Components](http://www.tecmint.com/wp-content/uploads/2017/03/iRedMail-Optional-Components.png)
|
||||
][13]
|
||||
|
||||
iRedMail Optional Components
|
||||
|
||||
18. On the next review screen type `Y` in order to apply configuration and start the installation process.
|
||||
|
||||
[
|
||||
![iRedMail Configuration Changes](http://www.tecmint.com/wp-content/uploads/2017/03/iRedMail-Configuration-Changes.png)
|
||||
][14]
|
||||
|
||||
iRedMail Configuration Changes
|
||||
|
||||
19. Finally, accept iRedMail scripts to automatically configure your machine firewall and MySQL configuration file by typing yes for all questions.
|
||||
|
||||
[
|
||||
![iRedMail System Configuration](http://www.tecmint.com/wp-content/uploads/2017/03/iRedMail-System-Configuration.png)
|
||||
][15]
|
||||
|
||||
iRedMail System Configuration
|
||||
|
||||
20. After the installation finishes the installer will provide some sensitive information, such as iRedAdmin credentials, web panel URL addresses and the file location with all parameters used at the installation process.
|
||||
|
||||
[
|
||||
![iRedMail Installation Summary](http://www.tecmint.com/wp-content/uploads/2017/03/iRedMail-Installation-Summary.png)
|
||||
][16]
|
||||
|
||||
iRedMail Installation Summary
|
||||
|
||||
Read the displayed information above carefully and reboot the machine in order to enable all mail services by issuing the following command.
|
||||
|
||||
```
|
||||
# init 6
|
||||
```
|
||||
|
||||
21. After the system reboots, login with an account with root privileges or as root and list all network sockets and their associated programs your mail server listens on by issuing the following command.
|
||||
|
||||
From the socket list you will see that your mail server covers almost all services required by a mail server to properly function: SMTP/S, POP3/S, IMAP/S and antivirus along with spam protection.
|
||||
|
||||
```
|
||||
# netstat -tulpn
|
||||
```
|
||||
[
|
||||
![iRedMail Network Sockets](http://www.tecmint.com/wp-content/uploads/2017/03/iRedMail-Network-Sockets.png)
|
||||
][17]
|
||||
|
||||
iRedMail Network Sockets
|
||||
|
||||
22. In order to view the location of all configuration files iRedMail has modified and the credentials used by iRedMail during the installation process for database administration, mail admin account and other accounts, display the contents of iRedMail.tips file.
|
||||
|
||||
The file is located in the directory where you’ve initially extracted the installation archive. Be aware that you should move and protect this file because it contains sensitive information about your mail server.
|
||||
|
||||
```
|
||||
# less iRedMail-0.9.6/iRedMail.tips
|
||||
```
|
||||
|
||||
23. The file mentioned above which contain details about your mail server will also be automatically mailed to the mail server administrator account, represented by the postmaster account.
|
||||
|
||||
The webmail can be accessed securely via HTTPS protocol by typing your machine IP address in a browser. Accept the error generated in browser by the iRedMail self-signed web certificate and log in with the password chosen for postmaster@your_domain.tld account during the initial installation. Read and store this e-mail to a safe mailbox.
|
||||
|
||||
```
|
||||
https://192.168.1.254
|
||||
```
|
||||
[
|
||||
![iRedMail Account Login](http://www.tecmint.com/wp-content/uploads/2017/03/iRedMail-Account-Login.png)
|
||||
][18]
|
||||
|
||||
iRedMail Account Login
|
||||
|
||||
[
|
||||
![iRedMail Web Mail](http://www.tecmint.com/wp-content/uploads/2017/03/iRedMail-Web-Mail.png)
|
||||
][19]
|
||||
|
||||
iRedMail Web Mail
|
||||
|
||||
That’s all! By now, you’ll have a full mail server configured on your premises which operates on its own, but not yet integrated with Samba4 Active Directory Domain Controller services.
|
||||
|
||||
On the next part we will see how to tamper iRedMail services (postfix, dovecot and roundcube configuration files) in order to query domain accounts, send, receive and read mail.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
作者简介:
|
||||
|
||||
I'am a computer addicted guy, a fan of open source and linux based system software, have about 4 years experience with Linux distributions desktop, servers and bash scripting.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: http://www.tecmint.com/install-iredmail-on-centos-7-for-samba4-ad-integration/
|
||||
|
||||
作者:[Matei Cezar][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/cezarmatei/
|
||||
|
||||
[1]:http://www.tecmint.com/install-samba4-active-directory-ubuntu/
|
||||
[2]:http://www.tecmint.com/centos-7-3-installation-guide/
|
||||
[3]:http://www.tecmint.com/install-samba4-active-directory-ubuntu/
|
||||
[4]:http://www.tecmint.com/wp-content/uploads/2017/03/Verify-CentOS-7-Hostname.png
|
||||
[5]:http://www.iredmail.org/download.html
|
||||
[6]:http://www.tecmint.com/wp-content/uploads/2017/03/iRedMail-Setup-Wizard.png
|
||||
[7]:http://www.tecmint.com/wp-content/uploads/2017/03/iRedMail-Mail-Storage-Path.png
|
||||
[8]:http://www.tecmint.com/wp-content/uploads/2017/03/iRedMail-Preferred-Web-Server.png
|
||||
[9]:http://www.tecmint.com/wp-content/uploads/2017/03/iRedMail-LDAP-Backend.png
|
||||
[10]:http://www.tecmint.com/wp-content/uploads/2017/03/iRedMail-LDAP-Suffix.png
|
||||
[11]:http://www.tecmint.com/wp-content/uploads/2017/03/iRedMail-Mail-Domain.png
|
||||
[12]:http://www.tecmint.com/wp-content/uploads/2017/03/iRedMail-Mail-Domain-Administrator.png
|
||||
[13]:http://www.tecmint.com/wp-content/uploads/2017/03/iRedMail-Optional-Components.png
|
||||
[14]:http://www.tecmint.com/wp-content/uploads/2017/03/iRedMail-Configuration-Changes.png
|
||||
[15]:http://www.tecmint.com/wp-content/uploads/2017/03/iRedMail-System-Configuration.png
|
||||
[16]:http://www.tecmint.com/wp-content/uploads/2017/03/iRedMail-Installation-Summary.png
|
||||
[17]:http://www.tecmint.com/wp-content/uploads/2017/03/iRedMail-Network-Sockets.png
|
||||
[18]:http://www.tecmint.com/wp-content/uploads/2017/03/iRedMail-Account-Login.png
|
||||
[19]:http://www.tecmint.com/wp-content/uploads/2017/03/iRedMail-Web-Mail.png
|
||||
[20]:http://www.tecmint.com/author/cezarmatei/
|
||||
[21]:http://www.tecmint.com/10-useful-free-linux-ebooks-for-newbies-and-administrators/
|
||||
[22]:http://www.tecmint.com/free-linux-shell-scripting-books/
|
@ -0,0 +1,74 @@
|
||||
GitHub对软件开发行业造成的冲击
|
||||
============================================================
|
||||
|
||||
![The impact GitHub is having on your software career](https://opensource.com/sites/default/files/styles/image-full-size/public/images/life/github-universe.jpg?itok=HCU81VX8 "The impact GitHub is having on your software career")
|
||||
>Image credits : From GitHub
|
||||
|
||||
在未来的12到24个月内(也就是说,在2018年,或者是2019年),人们雇佣软件开发者的方式将会发生彻底的改变。
|
||||
|
||||
2004 至 2014期间,我曾经就职于红帽,这是世界上最大的开源软件公司。还记得2004年七月的一天,我第一次来到这家公司,我的老板 Marty Messer 就跟我说,“所有你在这里所做的工作都会被开源,在未来,你将不需要任何的履历,因为所有的人都可以 Google 到你。”
|
||||
|
||||
供职于红帽的一个独特的好处是,在这种开源的工作期间,我们有机会建立自己的个人品牌和树立自己的声誉。我们可以通过邮件列表(mailling lists)和bug跟踪器(bug tracker)与其它的软件经理进行沟通,而且提交到 mercurial, subversion, 和CVS (并行版本控制系统)仓库的源代码都会被开源,并且通过google索引。
|
||||
|
||||
马上就到2017年了,我们将生活在一个处处充满开源的世界。
|
||||
|
||||
以下两点会让你对这个新时代有一个真正意义上的了解:
|
||||
|
||||
1. Microsoft(微软)在过去的一段很长的时间里都在坚持闭源,甚至是排斥开源。但是现在也从心底里开始拥抱开源了。它们成立了 .NET 基金会(红帽也是其中的一个成员),并且也加入了 Linux基金会。 .NET项目现在是以一个开源的形式在维护。
|
||||
2. Github 已经成为了一个独特的社交网络,解决了包括各种问题跟踪和源码的版本控制。
|
||||
|
||||
大多数的软件开发都是从闭源走过来的,他们可能还不知道发生了什么。对于他们来说 ,开源就意味着“将业余时间的所有努力成果都免费。”
|
||||
|
||||
对于我们这些在过去十年创造了一家十亿美元的开源软件公司的人来说,参与开源的以后就没有什么空闲的时间可言了。当然,为开源事业献身的好处也是很明显的,所得到的名誉是你自己的,并且会在各家公司之间传播。GitHub 是一个社交网络,在这个地方,你可以创建你的提交(commits)、你可以在你所专长的领域为一些全球性的组织做出贡献。临时做一些与工作无关的事情。
|
||||
|
||||
聪明的人会利用这种工作环境。他们会贡献他们的补丁(patches)、事件(issues)、评论(comments)给他们平时在工作中使用的语言和框架。包括TypeScript, .NET, 和Redux。他们也拥抱开源,并会尽可能多的开源他们的创新成果。甚至会提交他们的贡献给私人资料库。
|
||||
|
||||
GitHub 是一个很好的平衡器。比如说,你也许很难在澳大利亚得到一份来至印度的工作,但是,在GitHub上,却也没有什么可以阻止你在印度跟澳大利亚的工作伙伴一起工作。
|
||||
|
||||
在过去十年里,想从红帽获得一个工作机会的方式很简单。你只需要在一些小的方面,在红帽的软件经理开源的项目上做出一点你自己的贡献,直到他们觉得你在某些方面做出了很多有价值的贡献,而且也是一个很好的工作伙伴,那么你就可以得到一个红帽的工作机会了。(也许他们会邀请你)
|
||||
|
||||
现在,在不同的技术领域,开源给了我们所有人同样的机会,随着开源在世界的各处都流行开来,这样的事情将会在不同的地方盛行开来。
|
||||
|
||||
在[a recent interview][3]中,Linux 和 git的发明者Linux Torvalds(49K 粉丝,0 关注),这么说,“当你提交了很多小补丁,直到项目的维护者接受了你的补丁,从这一点说,你跟一般人不同的是,你不仅仅是提交了一些补丁,而是真正成为了这个组织里被信任的一部分。”
|
||||
|
||||
实际上你的名誉存在于那个你被信任的网络。我们都知道,当你离开一家公司以后,你的人脉和名誉可能会削弱,有的甚至 会丢失。就好像,你在一个小村庄里生活了很长的一段时间,这里所有的人都会知道你。然而,当你离开这个村庄,来到一个新的地方,这个地方可能没人听说过你,更糟糕的是,没有人知道任何知道你的人。
|
||||
|
||||
你曾经失去了第一次和第二次与世界连接的机会,甚至有可能会失去这第三个与世界交流的机会。除非你通过在会议或其他大型活动中演讲来建立自己的品牌,否则你通过在公司内部提交代码建立起来的信任早晚都会过去的,但是,如果你在GitHub上完成你的工作,这些东西依然全部都在,仍然可见,因为他连接到可信任的网络。
|
||||
|
||||
首先会发生的事情就是,一些弱势群体可能会利用这个。包括像学生,新毕业生、移民者--他们可能会利用这个“去往”澳大利亚。
|
||||
|
||||
这将会改变目前的现状。以前的一些开发人员可能会有过网络突然中断的情况,开源的一个原则是精英-最好的创意,最多的提交,最多的测试,和最快的落实执行,等等。
|
||||
|
||||
它并不完美,当然也没有什么事情是完美的,不能和伙伴一起工作,在人们对你的印象方面也会大打折扣。很多公司都会开除那些不能和团队和谐相处的人,而在GitHub工作的这些员工,他们主要是和其它的贡献者之间的交流。
|
||||
|
||||
GitHub不仅仅是一个代码仓库或是一个原始提交成员的列表,因为有些人总是用稻草人论点描述它。它是一个社交网络。我会这样说:GitHub有多少代码并不重要,重要的是有多少关于你代码的讨论。
|
||||
|
||||
GitHub 可以说是你的一个便捷的荣誉,并且在以后的12到24个月中,很多开发者使用它,而另外的一些依然并不使用,这将会形成一个很明显的差异。就像有电子邮件和没有电子邮件的区别(现在每个人都有电子邮件了),或者是有移动电话和没有移动电话的区别(现在每个人都有移动电话了),最终,绝大多数的人都会为开源工作,这将会是与别人的竞争中的一个差异化的优势。
|
||||
|
||||
但是现在,开发者的源码仓库已经被GitHub打乱了。
|
||||
|
||||
_[This article][1]首发于 Medium.com .转载请标明出处_
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
作者简介:
|
||||
|
||||
Josh Wulf - About me: I'm a Legendary Recruiter at Just Digital People; a Red Hat alumnus; a CoderDojo mentor; a founder of Magikcraft.io; the producer of The JDP Internship — The World's #1 Software Development Reality Show;
|
||||
|
||||
-----------------------
|
||||
|
||||
via: https://opensource.com/article/17/3/impact-github-software-career
|
||||
|
||||
作者:[Josh Wulf ][a]
|
||||
译者:[SysTick](https://github.com/SysTick)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:https://opensource.com/users/sitapati
|
||||
[1]:https://medium.com/@sitapati/the-impact-github-is-having-on-your-software-career-right-now-6ce536ec0b50#.dl79wpyww
|
||||
[2]:https://opensource.com/article/17/3/impact-github-software-career?rate=2gi7BrUHIADt4TWXO2noerSjzw18mLVZx56jwnExHqk
|
||||
[3]:http://www.theregister.co.uk/2017/02/15/think_different_shut_up_and_work_harder_says_linus_torvalds/
|
||||
[4]:https://opensource.com/user/118851/feed
|
||||
[5]:https://opensource.com/article/17/3/impact-github-software-career#comments
|
||||
[6]:https://opensource.com/users/sitapati
|
@ -0,0 +1,116 @@
|
||||
[为什么(大部分)高级语言运行效率较慢][7]
|
||||
============================================================
|
||||
|
||||
内容
|
||||
|
||||
|
||||
* * [回顾缓存消耗问题][1]
|
||||
* [为什么 C# 存在缓存未命中问题][2]
|
||||
* [垃圾回收][3]
|
||||
* [结语][5]
|
||||
|
||||
|
||||
在近两个个月中,我多次的和线上线下的朋友讨论了这个话题,所以我干脆直接把它写在博客中,以便以后查阅。
|
||||
|
||||
大部分高级语言运行效率较慢的原因通常有两点:
|
||||
|
||||
1. 没有很好的利用缓存;
|
||||
2. 垃圾回收机制性能消耗高。
|
||||
|
||||
但事实上,这两个原因可以归因于:高级语言强烈地鼓励编程人员分配很多的内存。
|
||||
|
||||
首先,下文内容主要讨论客户端应用。如果你的程序有 99.9% 的时间都在等待网络,那么这很可能不是拖慢语言运行效率的原因——优先考虑的问题当然是优化网络。在本文中,我们主要讨论程序在本地执行的速度。
|
||||
|
||||
我将选用 C# 语言作为本文的参考语言,其原因有二:首先它是我常用的高级语言;其次如果我使用 Java 语言,许多使用 C# 的朋友会告诉我 C# 不会有这些问题,因为它有值类型(但这是错误的)。
|
||||
|
||||
接下来我将会讨论,出于编程习惯编写的代码、使用普遍编程方法(with the grain)的代码或使用库或教程中提到的常用代码来编写程序时会发生什么。我对那些使用难搞的办法来解决语言自身毛病以“证明”语言没毛病这事没兴趣,当然你可以和语言抗争来避免它的毛病,但这并不能说明语言本身是没有问题的。
|
||||
|
||||
### 回顾缓存消耗问题
|
||||
|
||||
首先我们先来回顾一下合理使用缓存的重要性。下图是基于在 Haswell 架构下内存延迟对CPU影响的 [数据][10]:
|
||||
|
||||
![](https://www.sebastiansylvan.com/img/041315_0638_whymosthigh1.png)
|
||||
|
||||
针对这款 CPU 读取内存的延迟,CPU 需要消耗近 230 个运算周期从内存读取数据,同时需要消耗 4 个运算周期来读取 L1 缓冲区。因此错误的去使用缓存可导致运行速度拖慢近 50 倍。还好这并不是最糟糕的——在现代 CPU 中它们能同时地做多种操作,所以当你加载 L1 缓冲区内容的同时这个内容已经进入到了寄存器,因此数据从 L1 缓冲区加载这个过程的性能消耗就被完整的隐藏了起来。
|
||||
|
||||
撇开选择合理的算法不谈,不夸张地讲,在性能优化中你要考虑的最主要因素其实是缓存未命中。当你能够有效的访问一个数据时候,你才可以调整你的每个具体的操作。与缓存未命中的问题相比,那些次要的低效问题对运行速度并没有什么过多的影响。
|
||||
|
||||
这对于编程语言的设计者来说是一个好消息!你都_不必_去编写一个更高效的编译器,你可以完全摆脱一些额外的开销(比如:数组边界检查),你只需要专注怎么设计能使你的语言访问数据更高效,也不用担心与 C 语言代码比较运行速度。
|
||||
|
||||
### 为什么 C# 存在缓存未命中问题
|
||||
|
||||
坦率地讲 C# 在设计时就没打算在现代缓存中实现高效运行。我又一次提到程序语言设计的局限性以及其带给程序员无法编写高效的代码的“压力”。大部分的理论解决方法都非常的不便。我是在说那些编程语言“希望”你这样编写的习惯性代码。
|
||||
|
||||
C# 最基本的问题是对基础值类型(value-base)低下的支持性。其大部分的数据结构都是“内置”在语言内定义的(例如:栈,或其他内置对象)。但这些具有帮助性的内置结构体恰恰好会造成一些大问题。
|
||||
|
||||
* 你得把自己定义的结构体类型在最先声明——这意味着你如果需要用到这个类型作为堆分配,那么所有的结构体都会被堆分配。你也可以使用一些类包装器来打包你的结构体和其中的成员变量,但这十分的痛苦。如果类和结构体可以相同的方式声明,并且可根据具体情况来使用,这将是更好的。当数据可以作为值地存储在自定义的栈中,当这个数据需要被对分配时你就可以将其定义为一个对象,比如 C++ 就是这样工作的。因为只有少数的内容需要被堆分配,所以我们不鼓励所有的内容都被定义为对象类型。
|
||||
|
||||
* _引用_ 值被苛刻的限制。你可以将一个引用值传给函数,但只能这样。你不能直接引用 `List<int>` 中的元素,你必须先把所有的引用和索引全部存储下来。你不能直接取得指向栈、对象中的变量(或其他变量)的指针。你只能把他们复制一份,除了将他们传给一个函数(使用引用的方式)。当然这也是可以理解的。如果类型安全是一个先驱条件,灵活的引用变量和保证类型安全这两项要同时支持太难了(虽然这不可能)。
|
||||
|
||||
* [固定大小的缓冲区][6] 不支持自定义类型,而且还必须使用 `unsafe` 关键字。
|
||||
|
||||
* 有限的“数组切片”功能。虽然有提供 `ArraySegment` 类,但并没有人会使用它,这意味着如果只需要传递数组的一部分,你必须去创建一个 `IEnumerable` 对象,也就意味着要分配大小(包装)。就算接口接受 `ArraySegment` 对象作为参数,也是不够的——你只能用普通数组,而不能用 `List<T>`,也不能用 [栈数组][4] 等等。
|
||||
|
||||
最重要的是,除了非常简单的情况之外,C# 非常惯用堆分配。如果所有的数据都被堆分配,这意味着被访问时会造成缓存未命中(从你无法决定对象是如何在堆中存储开始)。所以当 C++ 程序面临着如何有效的组织数据在缓存中的存储这个挑战时,C# 则鼓励程序员去将数据分开的存放在一个个堆分配空间中。这就意味着程序员无法控制数据存储方式了,也开始产生不必要的缓存未命中问题,而导致性能急速的下降。[C# 已经支持原生编译][11] 也不会提升太多性能——毕竟在内存不足的情况下,提高代码质量本就杯水车薪。
|
||||
|
||||
存储是有开销的。在64位的机器上每个地址值占8位内存,而每次分配都会有存储元数据而产生的开销。与存储着少量大数据(以固定偏移的方式存储在其中)的堆相比,存储着大量小数据的堆(并且其中的数据到处都被引用)会产生更多的内存开销。尽管你可能不怎么关心内存怎么用,但事实上就是那些头部内容和地址信息导致堆变得臃肿,也就是在浪费缓存了,所以也造成了更多的缓存未命中,降低了代码性能。
|
||||
|
||||
当然有些时候也是有办法的,比如你可以使用一个很大的 `List<T>` 来构造数据池以存储分配你需要的数据和自己的结构体。这样你就可以方便的遍历或者批量更新你的数据池中的数据了。但这也会很混乱,因为无论你在哪要引用什么对象都要先能引用这个池,然后每次引用都需要做数组索引。从上文可以得出,在 C# 中做类似这样的处理的痛感比在 C++ 中做来的更痛,因为 C# 在设计时就是这样。此外,通过这种方式来访问池中的单个对象比直接将这个对象分配到内存来访问更加的昂贵——前者你得先访问池(这是个类)的地址,这意味着可能产生 _2_ 次缓存未命中。你还可以通过复制 `List<T>` 的结构形式来避免更多的缓存未命中问题,但这就更难搞了。我就写过很多类似的代码,自然这样的代码只会很低水平而且容易出错。
|
||||
|
||||
最后,我想说我指出的问题不仅是那些“热门”的代码。惯用手段编写的 C# 代码倾向于几乎所有地方都用类和引用。意思就是在你的代码中会均匀频率地随机出现数百次的运算周期损耗,使得操作的损耗似乎降低了。这虽然也可以被找出来,但你优化了这问题后,这还是一个 [均匀变慢][12] 的程序。
|
||||
|
||||
### 垃圾回收
|
||||
|
||||
在读下文之前我会假设你已经知道为什么在许多用例中垃圾回收是影响性能问题的重要原因。播放动画时总是随机的暂停通常都是大家都不能接受的吧。我会继续解释为什么设计语言时还加剧了这个问题。
|
||||
|
||||
因为 C# 在处理变量上的一些局限性,它不会鼓励你去使用大内存块分配来存储很多里面是内置对象的变量(可能存在栈中),这就使得你必须使用很多分配在堆中的小型类对象。说白了就是内存分配越多会导致花在垃圾回收上的时间就越多。
|
||||
|
||||
有些测评说 C# 或者 Java 是怎么在一些特定的例子中打败 C++ 的,其实是因为内存分配器都基于一种吞吐还算不错的垃圾回收机制(廉价的分配,允许统一的释放分配)。然而,这些测试场景都太特殊了。想要使 C# 的程序的内存分配率变得和那些非常普通的 C++ 程序都能达到的一样就必须要耗费更大的精力来编写它,所以这种比较就像是拿一个高度优化的管理程序和一个最简单原生的程序相比较一样。当你花同样的精力来写一个 C++ 程序时,肯定比你用 C# 来写性能好的多。
|
||||
|
||||
我还是相信你可以写出一套适用于高性能低延迟的应用的垃圾回收机制的(比如维护一个增量的垃圾回收,每次消耗固定的时间来做回收),但这还是不够的,大部分的高级语言在设计时就没考虑程序启动时就会产生大量的垃圾,这将会是最大的问题。当你就像写 C 一样习惯的去少去在 C# 分配内存,垃圾回收在高性能应用中可能就不会暴露出很多的问题了。而就算你 _真的_ 去实现了一个增量垃圾回收机制,这意味着你还可能需要为其做一个写屏障——这就相当于又消耗了一些性能了。
|
||||
|
||||
看看 `.Net` 库里那些基本类,内存分配几乎无处不在!我数了下,在 [.Net 核心框架][13] 中公共类比结构体的数量多出 19 倍之多,为了使用它们,你就得把这些东西全都弄到内存中去。就算是 `.Net` 框架的创造者们也无法抵抗设计语言时的警告啊!我都不知道怎么去统计了,使用基础类库时,你会很快意识到这不仅仅是值或对象的选择问题了,就算如此也还是 _伴随_ 着超级多的内存分配。这一切都让你觉得分配内存好像很容易一样,其实怎么可能呢,没有内存分配你连一个整形值都没法输出!不说这个,就算你使用预分配的 `StringBuilder`,你要是不用标准库来分配内存,也还不是连个整型都存不住。你要这么问我那就挺蠢的了。
|
||||
|
||||
当然还不仅仅是标准库,其他的 C# 库也一样。就算是 `Unity`(一个 _游戏引擎_,可能能多的关心平均性能问题)也会有一些全局返回已分配对象(或数组)的接口,或者强制调用时先将其分配内存再使用。举个例子,在一个 `GameObject` 中要使用 `GetComponents` 来调用一个数组,`Unity` 会强制地分配一个数组以便调用。就此而言,其实有许多的接口可以采用,但他们不选择,而去走常规路线来直接使用内存分配。写 `Unity` 的同胞们写的一手“好 C#”呀,但就是不那么高性能罢了。
|
||||
|
||||
# 结语
|
||||
|
||||
如果你在设计一门新的语言,拜托你可以考虑一下我提到的那些性能问题。在你创造出一款“足够聪明的编译器”之后这些都不是什么难题了。当然,没有垃圾回收器就要求类型安全很难。当然,没有一个规范的数据表示就创造一个垃圾回收器很难。当然,出现指向随机值的指针时难以去推出其作用域规则。当然,还有大把大把的问题摆在那里,然而解决了这些所有的问题,设计出来的语言就会是我们想的那样吗?那为什么这么多主要的语言都是在那些六十年代就已经被设计出的语言的基础上迭代的呢?
|
||||
|
||||
尽管你不能修复这些问题,但也许你可以尽可能的靠近?或者可以使用域类型(比如 `Rust` 语言)去保证其类型安全。或者也许可以考虑直接放弃“类型安全成本”去使用更多的运行时检查(如果这不会造成更多的缓存未命中的话,这其实没什么所谓。其实 C# 也有类似的东西,叫协变式数组,严格上讲是违背系统数据类型的,会导致一些运行时异常)。
|
||||
|
||||
如果你想在高性能场景中替代 C++,最基本的一点就是要考虑数据的存放布局和存储方式。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
作者简介:
|
||||
|
||||
我叫 Sebastian Sylvan。我来自瑞典,目前居住在西雅图。我在微软工作,研究全息透镜。诚然我的观点仅代表本人,与微软公司无关。
|
||||
|
||||
我的博客以图像、编程语言、性能等内容为主。联系我请点击我的 Twitter 或 E-mail。
|
||||
|
||||
------------
|
||||
|
||||
|
||||
via: https://www.sebastiansylvan.com/post/why-most-high-level-languages-are-slow
|
||||
|
||||
作者:[Sebastian Sylvan ][a]
|
||||
译者:[kenxx](https://github.com/kenxx)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:https://www.sebastiansylvan.com/about/
|
||||
[1]:https://www.sebastiansylvan.com/post/why-most-high-level-languages-are-slow/?imm_mid=0ee8ca&cmp=em-prog-na-na-newsltr_20170311#cache-costs-review
|
||||
[2]:https://www.sebastiansylvan.com/post/why-most-high-level-languages-are-slow/?imm_mid=0ee8ca&cmp=em-prog-na-na-newsltr_20170311#why-c-introduces-cache-misses
|
||||
[3]:https://www.sebastiansylvan.com/post/why-most-high-level-languages-are-slow/?imm_mid=0ee8ca&cmp=em-prog-na-na-newsltr_20170311#garbage-collection
|
||||
[4]:https://msdn.microsoft.com/en-us/library/vstudio/cx9s2sy4(v=vs.100).aspx
|
||||
[5]:https://www.sebastiansylvan.com/post/why-most-high-level-languages-are-slow/?imm_mid=0ee8ca&cmp=em-prog-na-na-newsltr_20170311#closing-remarks
|
||||
[6]:https://msdn.microsoft.com/en-us/library/vstudio/zycewsya(v=vs.100).aspx
|
||||
[7]:https://www.sebastiansylvan.com/post/why-most-high-level-languages-are-slow/
|
||||
[8]:https://www.sebastiansylvan.com/categories/programming-languages
|
||||
[9]:https://www.sebastiansylvan.com/categories/software-engineering
|
||||
[10]:http://www.7-cpu.com/cpu/Haswell.html
|
||||
[11]:https://msdn.microsoft.com/en-us/vstudio/dotnetnative.aspx
|
||||
[12]:http://c2.com/cgi/wiki?UniformlySlowCode
|
||||
[13]:https://github.com/dotnet/corefx
|
79
translated/tech/20170101 FTPS vs SFTP.md
Normal file
79
translated/tech/20170101 FTPS vs SFTP.md
Normal file
@ -0,0 +1,79 @@
|
||||
FTPS(基于 SSL 的FTP)与 SFTP(SSH 文件传输协议)对比
|
||||
================================================== ==========
|
||||
|
||||
![](http://www.techmixer.com/pic/2015/07/ftps-sftp.png)
|
||||
|
||||
**<ruby>SSH 文件传输协议<rt>SSH File transfer protocol</rt></ruby>、SFTP 或称为<ruby>通过安全套接层的文件传输协议<rt>File Transfer protocol via Secure Socket Layer</rt></ruby>**, 以及 FTPS 都是最常见的安全 FTP 通信技术,用于通过 TCP 协议将计算机文件从一个主机传输到另一个主机。SFTP 和 FTPS 都提供高级别文件传输安全保护,通过强大的算法(如 AES 和 Triple DES)来加密传输的数据。
|
||||
|
||||
但是 SFTP 和 FTPS 之间最显着的区别是如何验证和管理连接。
|
||||
|
||||
FTPS 是使用安全套接层(SSL)证书的 FTP 安全技术。整个安全 FTP 连接使用用户 ID,密码和 SSL 证书进行身份验证。一旦建立 FTPS 连接,如果服务器的证书是可信的,[FTP 客户端软件][6]将检查目的地[ FTP 服务器][7]。
|
||||
|
||||
如果证书由已知的证书颁发机构(CA)签发,或者证书由您的合作伙伴自己签发,并且您的信任密钥存储区中有其公开证书的副本,则 SSL 证书将被视为受信任的证书。FTPS 的所有用户名和密码信息将通过安全的 FTP 连接加密。
|
||||
|
||||
### 以下是 FTPS 的优点和缺点:
|
||||
|
||||
优点:
|
||||
|
||||
- 通信可以被人们读取和理解
|
||||
- 提供服务器到服务器文件传输的服务
|
||||
- SSL/TLS 具有良好的身份验证机制(X.509 证书功能)
|
||||
- FTP 和 SSL 支持内置于许多互联网通信框架中
|
||||
|
||||
缺点:
|
||||
|
||||
- 没有统一的目录列表格式
|
||||
- 需要辅助数据通道,这使得难以在防火墙后使用
|
||||
- 未定义文件名字符集(编码)的标准
|
||||
- 并非所有 FTP 服务器都支持 SSL/TLS
|
||||
- 没有标准的方式来获取和更改文件或目录属性
|
||||
|
||||
SFTP 或 SSH 文件传输协议是另一种安全的安全文件传输协议,设计为 SSH 扩展以提供文件传输功能,因此它通常仅使用 SSH 端口用于数据传输和控制。当 [FTP 客户端][8]软件连接到 SFTP 服务器时,它会将公钥传输到服务器进行认证。如果密钥匹配,提供任何用户/密码,身份验证就会成功。
|
||||
|
||||
### 以下是 SFTP 优点和缺点:
|
||||
|
||||
优点:
|
||||
|
||||
- 只有一个连接(不需要 DATA 连接)。
|
||||
- FTP 连接始终保持安全
|
||||
- FTP 目录列表是一致的和机器可读的
|
||||
- FTP 协议包括操作权限和属性操作,文件锁定和更多的功能。
|
||||
|
||||
缺点:
|
||||
|
||||
- 通信是二进制的,不能“按原样”记录下来用于人类阅读
|
||||
并且 SSH 密钥更难以管理和验证。
|
||||
- 这些标准定义了某些可选或推荐的选项,这会导致不同供应商的不同软件之间存在某些兼容性问题。
|
||||
- 没有服务器到服务器的副本和递归目录删除操作
|
||||
- 在 VCL 和 .NET 框架中没有内置的 SSH/SFTP 支持。
|
||||
|
||||
大多数 FTP 服务器软件这两种安全 FTP 技术都支持,以及强大的身份验证选项。
|
||||
|
||||
但 SFTP 将是防火墙赢家,因为它很友好。SFTP 只需要通过防火墙打开一个端口(默认为 22)。此端口将用于所有 SFTP 通信,包括初始认证,发出的任何命令以及传输的任何数据。
|
||||
|
||||
FTPS 通过紧密安全的防火墙相对难以实现,因为 FTPS 使用多个网络端口号。每次进行文件传输请求(get,put)或目录列表请求时,需要打开另一个端口号。因此,必须在您的防火墙中打开一系列端口以允许 FTPS 连接,这可能是您的网络的安全风险。
|
||||
|
||||
支持 FTPS 和 SFTP 的 FTP 服务器软件:
|
||||
|
||||
1. [Cerberus FTP服务器][2]
|
||||
2. [FileZilla - 最著名的免费 FTP 和 FTPS 服务器软件][3]
|
||||
3. [Serv-U FTP 服务器][4]
|
||||
|
||||
-------------------------------------------------- ------------------------------
|
||||
|
||||
via: http://www.techmixer.com/ftps-sftp/
|
||||
|
||||
作者:[Techmixer.com][a]
|
||||
译者:[Yuan0302](https://github.com/Yuan0302)
|
||||
校对:[jasminepeng](https://github.com/jasminepeng)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:http://www.techmixer.com/
|
||||
[1]:http://www.techmixer.com/ftps-sftp/#respond
|
||||
[2]:http://www.cerberusftp.com/
|
||||
[3]:http://www.techmixer.com/free-ftp-server-best-windows-ftp-server-download/
|
||||
[4]:http://www.serv-u.com/
|
||||
[6]:http://www.techmixer.com/free-ftp-file-transfer-protocol-softwares/
|
||||
[7]:http://www.techmixer.com/free-ftp-server-best-windows-ftp-server-download/
|
||||
[8]:http://www.techmixer.com/best-free-mac-ftp-client-connect-ftp-server/
|
@ -0,0 +1,75 @@
|
||||
## 如何阻止黑客入侵你的Linux机器之2:另外三个建议
|
||||
|
||||
![security tips](https://www.linux.com/sites/lcom/files/styles/rendered_file/public/security-tips.jpg?itok=JMp34oc3 "security tips")
|
||||
|
||||
在这个系列中, 我们会讨论一些重要信息来阻止黑客入侵你的系统。观看这个免费的网络研讨会on-demand获取更多的信息。[Creative Commons Zero][1]Pixabay
|
||||
|
||||
在这个系列的[第一部分][3]中,我分享过其他两种简单的方法来阻止黑客黑掉你的Linux主机。这里是另外三条来自于我最近的Linux基础网络研讨会的建议,在这次研讨会中,我分享了更多的黑客用来入侵你的主机的策略、工具和方法。完整的[研讨会on-demand][4]视频可以在网上免费观看。
|
||||
|
||||
### 简单的Linux安全建议 #3
|
||||
** Sudo. **
|
||||
|
||||
Sudo是非常、非常的重要。我认为这只是很基本的东西,但就是这些基本的东西让我黑客的生活变得更困难。如果你没有配置sdo,还请配置好它。
|
||||
|
||||
还有,你主机上所有的用户必须使用他们自己的密码。不要都免密码使用sudo执行所有命令,那样做毫无意义,除了让我黑客的生活变得更简单,这时我能获取一个不需要密码就能以sudo方式运行所有命令的帐号。如果我可以使用sudo命令而且不需要再次确认,那么我就能入侵你,同时当我获得你的没有使用密码的SSH密钥后,我就能十分容易的开始任何黑客活动。现在,我已经拥有了你机器的root权限。
|
||||
|
||||
保持较低的超时时间。我们喜欢劫持用户的会话,如果你的某个用户能够使用sudo,并且设置的超时时间是3小时,当我劫持了你的会话,那么你就再次给了我一个自由的通道,哪怕你需要一个密码。
|
||||
|
||||
我推荐超时时间大约为10分钟,甚至是5分钟。用户们将需要反复地输入他们的密码,但是,如果你设置了较低的超时时间,你将减少你的受攻击面。
|
||||
|
||||
还要限制可以访问的命令和禁止通过sudo来访问shell。大多数Linux发行版目前默认允许你使用"sudo bash"来获取一个root身份的shell,当你需要做大量的系统管理的任务时,这种机制是非常好的。然而,应该对大多数用户实际需要运行的命令有一个限制。你对他们限制越多,你主机的受攻击面就越小。如果你允许我shell访问,我将能够做任何类型的事情。
|
||||
|
||||
### 简单的Linux安全建议 #4
|
||||
|
||||
** 限制正在运行的服务 **
|
||||
|
||||
防火墙是很棒的。你的边界防火墙非常的强大。当流量流经你的网络时,防火墙外面的几家制造商做着极不寻常的工作。但是防火墙内的人呢?
|
||||
|
||||
你正在使用基于主机的防火墙或者基于主机的入侵检测系统吗?如果是,请正确配置好它。怎样可以知道你的正在受到保护的东西是否出了问题呢?
|
||||
|
||||
答案是限制当前正在运行的服务。不要在不需要提供mySQL服务的机器上运行它。如果你有一个默认会安装完整的LAMP套件的Linux发行版,而你不会在它上面运行任何东西,那么卸载它。禁止那些服务,不要开启它们。
|
||||
|
||||
同时确保用户没有默认的证书,确保那些内容已被安全地配置。如何你正在运行Tomcat,你不被允许上传你自己的小程序。确保他们不会以root的身份运行。如果我能够运行一个小程序,我不想以管理员的身份来运行它,也不想我自己访问权限。你对人们能够做的事情限制越多,你的机器就将越安全。
|
||||
|
||||
### 简单的Linux安全建议 #5
|
||||
|
||||
** 小心你的日志记录 **
|
||||
|
||||
看看它们,认真地,小心你的日志记录。六个月前,我们遇到一个问题。我们的一个顾客从来不去看日志记录,尽管他们已经拥有了很久、很久的日志记录。假如他们曾经看过日志记录,他们就会发现他们的机器早就已经被入侵了,并且他们的整个网络都是对外开放的。我在家里处理的这个问题。每天早上起来,我都有一个习惯,我会检查我的email,我会浏览我的日志记录。这仅花费我15分钟,但是它却能告诉我很多关于什么正在发生的信息。
|
||||
|
||||
就在这个早上,机房里的三台电脑死机了,我不得不去重启它们。我不知道为什么会出现这样的情况,但是我可以从日志记录里面查出什么出了问题。它们是实验室的机器,我并不关心它们,但是有人需要关心。
|
||||
|
||||
通过Syslog、Splunk或者任何其他日志整合工具将你的日志进行集中是极佳的选择。这比将日志保存在本地要好。我最喜欢做是事情就是修改你的日志记录让你不知道我曾经入侵过你的电脑。如果我做了那,你将不会有任何线索。对我来说,修改集中的日志记录比修改本地的日志更难。
|
||||
|
||||
就像你的很重要的人,送给他们鲜花,磁盘空间。确保你有足够的磁盘空间用来记录日志。进入一个只能读的文件系统不是一件很好的事情。
|
||||
|
||||
还需要知道什么是不正常的。这是一件非常困难的事情,但是从长远来看,这将使你日后受益匪浅。你应该知道什么正在进行和什么时候出现了一些异常。确保你知道那。
|
||||
|
||||
在[第三封和最后的博客][5]里,我将就这次研讨会中问到的一些比较好的安全问题进行回答。[现在开始看这个完整的免费的网络研讨会on-demand][6]吧。
|
||||
|
||||
|
||||
*** Mike Guthrie 就职于能源部,主要做红队交战和渗透测试 ***
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
||||
via: https://www.linux.com/news/webinar/2017/how-keep-hackers-out-your-linux-machine-part-2-three-more-easy-security-tips
|
||||
|
||||
作者:[MIKE GUTHRIE][a]
|
||||
译者:[zhousiyu325](https://github.com/zhousiyu325)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
|
||||
[a]:https://www.linux.com/users/anch
|
||||
[1]:https://www.linux.com/licenses/category/creative-commons-zero
|
||||
[2]:https://www.linux.com/files/images/security-tipsjpg
|
||||
[3]:https://www.linux.com/news/webinar/2017/how-keep-hackers-out-your-linux-machine-part-1-top-two-security-tips
|
||||
[4]:http://portal.on24.com/view/channel/index.html?showId=1101876&showCode=linux&partnerref=linco
|
||||
[5]:https://www.linux.com/news/webinar/2017/how-keep-hackers-out-your-linux-machine-part-3-your-questions-answered
|
||||
[6]:http://bit.ly/2j89ISJ
|
||||
|
||||
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
Linux 命令行导航贴士:pushd 和 popd 命令基础
|
||||
Linux 命令行工具使用小贴士及技巧(二):pushd 和 popd 命令基础
|
||||
============================================================
|
||||
|
||||
在本系列的[第一部分][4]中,我们通过讨论 **cd -** 命令的用法,重点介绍了 Linux 中的命令行导航。还讨论了一些其他相关要点/概念。现在进一步讨论,在本文中,我们将讨论如何使用 pushd 和 popd 命令在 Linux 命令行上获得更快的导航体验。
|
||||
|
@ -1,15 +1,8 @@
|
||||
[HaitaoBio](https://github.com/HaitaoBio) translating...
|
||||
Linux 命令行工具使用小贴士及技巧(三) - 环境变量 CDPATH
|
||||
|
||||
Linux 命令行工具使用小贴士及技巧 3 - 环境变量 CDPATH
|
||||
============================================================
|
||||
|
||||
### 文章导航
|
||||
|
||||
1. [The CDPATH environment variable 环境变量 CDPATH ][1]
|
||||
2. [Points to keep in mind 要点][2]
|
||||
3. [Conclusion 总结][3]
|
||||
|
||||
在这个系列的第一部分,我们详细地讨论了 `cd -` 命令,并且在第二部分,我们深入探究了 `pushd` 和 `popd` 两个命令
|
||||
在这个系列的第一部分,我们详细地讨论了 `cd -` 命令,在第二部分,我们深入探究了 `pushd` 和 `popd` 两个命令,以及它们使用的场景。
|
||||
|
||||
继续对命令行的讨论,在这篇教程中,我们将会通过简单易懂的实例来讨论 `CDPATH` 这个环境变量。我们也会讨论关于此变量的一些进阶细节。
|
||||
|
||||
@ -17,44 +10,49 @@ _在这之前,先声明一下此教程中的所有实例都已经在 Ubuntu 14
|
||||
|
||||
### 环境变量 CDPATH
|
||||
|
||||
即使你的命令行在特定的目录下 - 但也必须在切换目录时提供绝对路径。比如,在 _/home/himanshu/Downloads_ 目录下确认当前的目录位置:
|
||||
即使你的命令行所有操作都在特定的目录下 - 例如你的主目录 - 在切换目录时你也不得不提供绝对路径。比如,考虑我现在的情况,就是在 _/home/himanshu/Downloads_ 目录下:
|
||||
|
||||
```sh
|
||||
```
|
||||
$ pwd
|
||||
/home/himanshu/Downloads
|
||||
```
|
||||
|
||||
要切换至 _/home/himanshu/Desktop_ 目录,我一般会这样做:
|
||||
现在要求切换至 _/home/himanshu/Desktop_ 目录,我一般会这样做:
|
||||
|
||||
```sh
|
||||
cd /home/himanshu/Desktop/
|
||||
```
|
||||
|
||||
或者
|
||||
|
||||
```sh
|
||||
cd ~/Desktop/
|
||||
```
|
||||
|
||||
或者
|
||||
|
||||
```sh
|
||||
cd ../Desktop/
|
||||
```
|
||||
|
||||
能不能只是运行以下命令就能简单地实现呢:
|
||||
|
||||
```sh
|
||||
cd Desktop
|
||||
```
|
||||
|
||||
是的,这完全有可能。这就是环境变量 `CDPATH` 出现的时候了。你可使用这个变量来为 `cd` 命令定义基础目录。
|
||||
|
||||
如果你想尝试打印它的值,你会看见这个环境变量默认是空值的:
|
||||
如果你尝试打印它的值,你会看见这个环境变量默认是空值的:
|
||||
|
||||
```sh
|
||||
$ echo $CDPATH
|
||||
$
|
||||
```
|
||||
|
||||
现在 ,考虑到已经详细地对它进行了讨论,接着让我们定义这个环境变量为 _/home/himanshu_,作为 `cd` 命令的基础目录来使用把。
|
||||
现在 ,考虑到上面提到的场景,我们使用这个环境变量,将 _/home/himanshu_ 作为 `cd` 命令的基础目录来使用。
|
||||
|
||||
最简单的做法时这样:
|
||||
最简单的做法这样:
|
||||
|
||||
```sh
|
||||
export CDPATH=/home/himanshu
|
||||
@ -74,7 +72,7 @@ $
|
||||
|
||||
### 要点
|
||||
|
||||
现在你应该知道是怎样利用环境变量 CDPATH 来简易地在 _/home/himanshu/Downloads_ 和 _/home/himanshu/Desktop_ 之间切换。现在,考虑以下这种情况, 在 _/home/himanshu/Desktop_ 目录里包含一个名字叫做 _Downloads_ 的子目录,这是之后将要切换到的目录。
|
||||
现在你应该知道了怎样利用环境变量 CDPATH 在 _/home/himanshu/Downloads_ 和 _/home/himanshu/Desktop_ 之间轻松切换。现在,考虑以下这种情况, 在 _/home/himanshu/Desktop_ 目录里包含一个名字叫做 _Downloads_ 的子目录,这是将要切换到的目录。
|
||||
|
||||
但突然你会意识到 _cd Desktop_ 会切换到 _/home/himanshu/Desktop_。所以,为了确保这不会发生,你可以这样做:
|
||||
|
||||
@ -82,21 +80,21 @@ $
|
||||
cd ./Downloads
|
||||
```
|
||||
|
||||
当上述的命令执行没有问题时,就会带来额外的效果( 无论这有多大 ),特别是考虑到你在类似的情况发生时必须这样做。所以,有一个更加优雅的解决方案来处理,就是以如下方式来设定 `CDPATH` 环境变量。
|
||||
虽然上述命令本身没有问题,但你还是需要耗费点额外的精力( 虽然很小 ),尤其是每次这种情况发生时你都不得不这样做。所以,有一个更加优雅的解决方案来处理,就是以如下方式来设定 `CDPATH` 环境变量。
|
||||
|
||||
```sh
|
||||
export CDPATH=".:/home/himanshu"
|
||||
```
|
||||
|
||||
这意味这你在告诉 `cd` 命令先在当前的工作目录查找该目录,然后再尝试搜寻 _/home/himanshu_ 目录。当然, `cd` 命令是否以这样的方式运行,完全取决于你的偏好和要求 - 我在这个讨论下的提起这些是为了让你知道这种情况也有可能发生。
|
||||
它的意思是告诉 `cd` 命令先在当前的工作目录查找该目录,然后再尝试搜寻 _/home/himanshu_ 目录。当然, `cd` 命令是否以这样的方式运行,完全取决于你的偏好和要求 - 讨论这一点的目的是为了让你知道这种情况可能会发生。
|
||||
|
||||
就如你现在所知道的,一旦环境变量 `CDPATH` 被设置,它的值 - 或者它所包含的路径集合 - 就是系统中给 `cd` 命令搜索目录的地方 ( 除了包含了你所使用的绝对路径 )
|
||||
就如你现在所知道的,一旦环境变量 `CDPATH` 被设置,它的值 - 或者它所包含的路径集合 - 就是系统中 `cd` 命令搜索目录的地方 ( 当然除了使用绝对路径的场景 )。所以,完全取决于你来确保该命令行为的一致性。
|
||||
|
||||
继续说,如果一个 bash 脚本以相对路径使用 `cd` 命令的话,最好还是先清除或者重置环境变量 `CDPATH`,除非你觉得遇上不可预测的麻烦也无所谓。还有一个可选的方法,比起在终端使用 `export` 命令来设置 `CDPATH`,你可以在测试完交互式/非交互式 shell 之后,在你的 `.bashrc` 文件里设置环境变量,这样可以确保你对环境变量的改动只对交互式 shell 生效。
|
||||
继续说,如果一个 bash 脚本以相对路径使用 `cd` 命令,最好还是先清除或者重置环境变量 `CDPATH`,除非你觉得遇上不可预测的麻烦也无所谓。还有一个可选的方法,比起在终端使用 `export` 命令来设置 `CDPATH`,你可以在测试完交互式/非交互式 shell 之后,在你的 `.bashrc` 文件里设置环境变量,这样可以确保你对环境变量的改动只对交互式 shell 生效。
|
||||
|
||||
环境变量中,路径出现的顺序同样也是很重要。举个例子,如果当前目录是在 _/home/himanshu_ 目录之前列出来,`cd` 命令就会先搜索当前的工作目录然后才会移动到 _/home/himanshu_ 目录。然而,如果该值为 _"/home/himanshu:."_,搜索就首先从 _/home/himanshu_ 开始,然后到当前目录。不必要地说一句,这会影响 `cd` 命令的行为,并且不注意路径的顺序可能会导致一些麻烦。
|
||||
环境变量中,路径出现的顺序同样也是很重要。举个例子,如果当前目录是在 _/home/himanshu_ 目录之前列出来,`cd` 命令就会先搜索当前的工作目录然后才会移动到 _/home/himanshu_ 目录。然而,如果该值为 _"/home/himanshu:."_,搜索就首先从 _/home/himanshu_ 开始,然后到当前目录。不用说,这会影响 `cd` 命令的行为,并且不注意路径的顺序可能会导致一些麻烦。
|
||||
|
||||
要牢记在心的是,环境变量 `CDPATH`,就像其名字表达的,只对 `cd` 命令有作用。意味着在 _/home/himanshu/Downloads_ 目录里面时,你能运行 `_cd Desktop_` 命令来切换到 _/home/himanshu/Desktop_ 目录,当你不能使用 `ls`。以下是一个例子:
|
||||
要牢记在心的是,环境变量 `CDPATH`,就像其名字表达的,只对 `cd` 命令有作用。意味着在 _/home/himanshu/Downloads_ 目录里面时,你能运行 `_cd Desktop_` 命令来切换到 _/home/himanshu/Desktop_ 目录,但你不能使用 `ls`。以下是一个例子:
|
||||
|
||||
```sh
|
||||
$ pwd
|
||||
@ -106,7 +104,7 @@ ls: cannot access Desktop: No such file or directory
|
||||
$
|
||||
```
|
||||
|
||||
然而,这还是有点用处的。例如,我们可以用以下方式来便捷地打包文件:
|
||||
然而,这还是有简单的变通处理的。例如,我们可以用以下不怎么费力的方式来达到目的:
|
||||
|
||||
```sh
|
||||
$ cd Desktop/;ls
|
||||
@ -114,24 +112,25 @@ $ cd Desktop/;ls
|
||||
backup backup~ Downloads gdb.html outline~ outline.txt outline.txt~
|
||||
```
|
||||
|
||||
好了,这样就能在任何情况下运行了。
|
||||
不过,不是每种情况就能变通处理的。
|
||||
|
||||
另一个重点是: 就像你观察到的,无论你使用 `CDPATH` 环境变量集来运行 `cd` 命令,该命令都会在输出里产生你切换到的目录的完整路径。顺带一说,不是所有人都想在每次运行 `cd` 命令时都看到这些信息。
|
||||
另一个重点是: 就像你可能已经观察到的,每次你使用 `CDPATH` 环境变量集来运行 `cd` 命令时,该命令都会在输出里显示你切换到的目录的完整路径。不用说,不是所有人都想在每次运行 `cd` 命令时看到这些信息。
|
||||
|
||||
为了确保输出能偶被制止,你可以使用以下命令:
|
||||
为了确保该输出被制止,你可以使用以下命令:
|
||||
|
||||
```sh
|
||||
alias cd='>/dev/null cd'
|
||||
```
|
||||
无论 `cd` 命令是否运行成功,上述的命令的不会输出任何东西,但无论命令是否失败都能允许产生错误信息。
|
||||
|
||||
最后,假如你遇到设置 CDPATH 环境变量后忘记其值,你不应该使用 shell 的 tab 自动补全功能,应该尝试安装并启动 bash 自动补全( bash-completion )。更多请参考 [here][4]。
|
||||
如果 `cd` 命令运行成功,上述命令不会输出任何东西,如果失败,则允许产生错误信息。
|
||||
|
||||
最后,假如你遇到设置 CDPATH 环境变量后,不能使用 shell 的 tab 自动补全功能的问题,可以尝试安装并启动 bash 自动补全( bash-completion )。更多请参考 [这里][4]。
|
||||
|
||||
### 总结
|
||||
|
||||
`CDPATH` 环境变量时一把双刃剑,如果没有掌握完善的知识和不注意地使用,会令你陷入困境,并花费你大量宝贵时间去解决问题。当然,这不代表你不应该试一下;如果你决定要使用 `CDPATH` 时,只需要了解一下所有的可用选项,就会带来很大的帮助,然后继续使用它。
|
||||
`CDPATH` 环境变量时一把双刃剑,如果没有掌握完善的知识和随意使用,可能会令你陷入困境,并花费你大量宝贵时间去解决问题。当然,这不代表你不应该试一下;只需要了解一下所有的可用选项,如果你得出结论,使用 CDPATH 会带来很大的帮助,就继续使用它吧。
|
||||
|
||||
你已经能够熟练地使用 `CDPATH` 了吗,你有更多的贴士要分享?请在评论区里发表一下你的想法吧。
|
||||
你已经能够熟练地使用 `CDPATH` 了吗?你有更多的贴士要分享?请在评论区里发表一下你的想法吧。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
@ -1,14 +1,14 @@
|
||||
# [用于 Ubuntu 和 Fedora 上的 10 个最好的 Linux 终端仿真器][12]
|
||||
# [Ubuntu 和 Fedora 上 10 个最好的 Linux 终端仿真器][12]
|
||||
|
||||
[
|
||||
![10 Best Linux Terminals](http://www.linuxandubuntu.com/uploads/2/1/1/5/21152474/10-best-linux-terminals_orig.jpg)
|
||||
][3]
|
||||
|
||||
对于 Linux 用户来说,最重要的应用程序之一就是终端仿真器。它允许每个用户获得对 shell 的访问。Bash 是 Linux 和 UNIX 发行版中最常用的 shell,它很强大,并且对于新手和高级用户来说,掌握 bash 都很有必要。因此,在这篇文章中,你将知晓一个 Linux 用户应作出的伟大选择,那就是你必须使用一个优秀的终端仿真器。
|
||||
对于 Linux 用户来说,最重要的应用程序之一就是终端仿真器。它允许每个用户获得对 shell 的访问。Bash 是 Linux 和 UNIX 发行版中最常用的 shell,它很强大,对于新手和高级用户来说,掌握 bash 都很有必要。因此,在这篇文章中,你可以了解 Linux 用户有哪些优秀的终端仿真器可以选择。
|
||||
|
||||
### 1、Terminator
|
||||
|
||||
这个项目的目标是创造一个用于排列终端的有用工具。它受到一些程序比如 gnome-multi-term、quadkonsole 等的鼓舞。它的重点是以网格的形式排列终端。
|
||||
这个项目的目标是创造一个能够很好排列终端的有用工具。它受到一些如 gnome-multi-term、quadkonsole 等程序的启发,重点是以网格的形式排列终端。
|
||||
|
||||
#### 特性浏览
|
||||
|
||||
@ -34,14 +34,15 @@ sudo apt-get install terminator
|
||||
**Tilda** 的独特之处在于它不像一个普通的窗口,相反,你可以使用一个特殊的热键从屏幕的顶部上下拖动它。
|
||||
另外,Tilda 是高度可配置的,可以自定义绑定热键,改变外观,以及其他许多能够影响 Tilda 特性的选项。
|
||||
|
||||
在 Ubuntu 和 Fedora 上都可以使用包管理器安装 Tilda,当然,你也可以查看它的 [GitHub 仓库][14]
|
||||
在 Ubuntu 和 Fedora 上都可以使用包管理器安装 Tilda,当然,你也可以查看它的 [GitHub 仓库][14]。
|
||||
|
||||
![tilda linux terminal](http://www.linuxandubuntu.com/uploads/2/1/1/5/21152474/tilda-linux-terminal_orig.png)
|
||||
][5]Also read - [Terminator Emulator With Multiple Terminals In One Window][1]
|
||||
[
|
||||
![tilda linux terminal](http://www.linuxandubuntu.com/uploads/2/1/1/5/21152474/tilda-linux-terminal_orig.png)
|
||||
][5]
|
||||
|
||||
### 3、Guake
|
||||
|
||||
Guake 是另一个和 Tilda 或 yakuake 类似的可拖动终端仿真器。如果你知道一些关于 Python、Git 和 GTK 的知识的话,你可以给 Guake 添加一些新的特性。
|
||||
Guake 是一个和 Tilda 或 yakuake 类似的可拖动终端仿真器。如果你知道一些关于 Python、Git 和 GTK 的知识的话,你可以给 Guake 添加一些新的特性。
|
||||
|
||||
Guake 在许多发行版上均可用,所以如果你想安装它,你可以查看你的版本仓库。
|
||||
|
||||
@ -51,7 +52,7 @@ Guake 在许多发行版上均可用,所以如果你想安装它,你可以
|
||||
* 简单、容易且很优雅
|
||||
* 从终端到 GUI 的流畅集成
|
||||
* 当你使用的时候出现,一旦按下预定义热键便消失(默认情况下是 F12)
|
||||
* Compiz 提供透明背景支持
|
||||
* Compiz 透明支持
|
||||
* 多重 Tab
|
||||
* 丰富的调色板
|
||||
* 还有更多……
|
||||
@ -63,7 +64,9 @@ Guake 在许多发行版上均可用,所以如果你想安装它,你可以
|
||||
|
||||
[
|
||||
![roxterm linux terminal](http://www.linuxandubuntu.com/uploads/2/1/1/5/21152474/roxterm-linux-terminal_orig.png)
|
||||
][6][http://roxterm.sourceforge.net/index.php?page=index&lang=en][16]
|
||||
][6]
|
||||
|
||||
[http://roxterm.sourceforge.net/index.php?page=index&lang=en][16]
|
||||
|
||||
### 5、XTerm
|
||||
|
||||
@ -71,15 +74,17 @@ Xterm 是 Linux 和 UNIX 系统上最受欢迎的终端仿真器,因为它是
|
||||
|
||||
[
|
||||
![xterm linux terminal](http://www.linuxandubuntu.com/uploads/2/1/1/5/21152474/published/xterm-linux-terminal.png?1487083067)
|
||||
][7]Also read - [Guake Another Linux Terminal Emulator][2]
|
||||
][7]
|
||||
|
||||
### 6、Eterm
|
||||
|
||||
如果你正在寻找一个漂亮、强大的终端仿真器,那么 Eterm 是你最好的选择。Eterm 是一个使用 vt102 终端格式的终端仿真器,它被当作 Xterm 的替代品。它的设计伴有哲学的选择自由,留有大量的权利、灵活性和自由给用户,一切可能尽在用户手中。
|
||||
如果你正在寻找一个漂亮、强大的终端仿真器,那么 Eterm 是你最好的选择。Eterm 是一个彩色 vt102 终端仿真器,被当作是 Xterm 的替代品。它按照自由选择的哲学思想进行设计,将尽可能多的权利、灵活性和自由交到用户手中。
|
||||
|
||||
[
|
||||
![etern linux terminal](http://www.linuxandubuntu.com/uploads/2/1/1/5/21152474/published/etern-linux-terminal.jpg?1487083129)
|
||||
][8]Official Website: [http://www.eterm.org/][17]
|
||||
][8]
|
||||
|
||||
官网: [http://www.eterm.org/][17]
|
||||
|
||||
### 7、Gnome Terminal
|
||||
|
||||
@ -93,7 +98,7 @@ Gnome Terminal 是最受欢迎的终端仿真器之一,它被许多 Linux 用
|
||||
|
||||
### 8、Sakura
|
||||
|
||||
Sakura 是一个基于 GTK 和 VTE 的终端仿真器。它是一个只有很少依赖的终端仿真器,所以你不需要有一个完整的 GNOME 桌面才能够安装一个像样的终端仿真器。
|
||||
Sakura 是一个基于 GTK 和 VTE 的终端仿真器。它是一个只有很少依赖的终端仿真器,所以你不需要先安装一个完整的 GNOME 桌面才能有一个像样的终端仿真器。
|
||||
|
||||
你可以使用你的包管理器来安装它,因为 Sakura 在绝大多数发行版中都是可用的。
|
||||
|
||||
@ -116,9 +121,9 @@ LilyTerm 是一个基于 libvte 的终端仿真器,旨在快速和轻量,是
|
||||
|
||||
### 10、Konsole
|
||||
|
||||
如果你是一名 KDE 或 Plasma 用户,那么你一定知道 Konsole,因为它是 KDE 桌面的默认终端仿真器,也是我最喜爱的终端仿真器之一,因为它很舒适,很有用,
|
||||
如果你是一名 KDE 或 Plasma 用户,那么你一定知道 Konsole,因为它是 KDE 桌面的默认终端仿真器,也是我最喜爱的终端仿真器之一,因为它很舒适易用。
|
||||
|
||||
它在 Ubuntu 和 fedora 上均可用,但如果你使用 Unity 桌面,那么你需要选择别的终端仿真器,或者你可以考虑使用 Kubuntu 。
|
||||
它在 Ubuntu 和 fedora 上均可用,但如果你在使用 Ubuntu (Unity),那么你需要选择别的终端仿真器,或者你可以考虑使用 Kubuntu 。
|
||||
|
||||
[
|
||||
![konsole linux terminal](http://www.linuxandubuntu.com/uploads/2/1/1/5/21152474/editor/konsole-linux-terminal.png?1487083345)
|
||||
@ -126,7 +131,7 @@ LilyTerm 是一个基于 libvte 的终端仿真器,旨在快速和轻量,是
|
||||
|
||||
### 结论
|
||||
|
||||
我们是 Linux 用户,所以出于个人目的,我们可以有许多选择来挑选更好的应用。因此,你应该选择**最好的终端**来满足个人需求,虽然你也可以选择另一个 shell 来满足个人需求,比如你也可以使用 fish shell(鱼壳)。
|
||||
我们是 Linux 用户,根据自己的需求,可以有许多选择来挑选更好的应用。因此,你可以选择**最好的终端**来满足个人需求,虽然你也可以选择另一个 shell 来满足个人需求,比如你也可以使用 fish shell。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
@ -134,7 +139,7 @@ via: http://www.linuxandubuntu.com/home/10-best-linux-terminals-for-ubuntu-and-f
|
||||
|
||||
作者:[Mohd Sohail][a]
|
||||
译者:[ucasFL](https://github.com/ucasFL)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
校对:[jasminepeng](https://github.com/jasminepeng)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
|
@ -0,0 +1,56 @@
|
||||
|
||||
# 这个 Xfce 的 Bug 会毁坏用户的显示器
|
||||
|
||||
在 Linux 上使用 Xfce 桌面环境可以很快速和灵活的 — 但是它目前遭受着一个很严重的缺陷影响。
|
||||
|
||||
有用户报告说这个轻量级替代 GNOME 和 KDE 的 Xfce 桌面选用默认桌面壁纸会造成笔记本电脑显示器和液晶显示器的损坏。
|
||||
|
||||
有确凿的摄影证据来要求索赔。
|
||||
|
||||
### Xfce Bug #12117
|
||||
|
||||
_“桌面默认开机画面造成显示器损坏!”_ 某用户在 Xfce 社区的 Bugzilla (译者注:Bugzilla 是一个开源的缺陷跟踪系统) 尖叫似的 [一个Bug 提交][1] 。
|
||||
|
||||
_“默认桌面壁纸被我的动物抓破 (<ruby>猛烈攻击<rt>sic</rt></ruby>) (译者注:sic 在原话里是指猛烈攻击,应该也指碳化硅晶片,这是应用于 LED 的材料) 导致全部塑料从我的液晶显示器掉落!能让我们选择不同的壁纸吗?我不想再有划痕,谁想呢(译者注:原文是 whu not,可能想打 who not,也许因屏幕坏了太激动打错字了)?让我们在这里结束这老鼠游戏。”_
|
||||
|
||||
[
|
||||
![](http://www.omgubuntu.co.uk/wp-content/uploads/2017/03/cat-xfce-bug-2-750x801.jpg)
|
||||
][6]
|
||||
|
||||
这缺陷 — 或者说是这爪? — 不只是单独一个用户的桌面遇到问题。其他用户都能重现这个问题了,尽管不一致,在这第二个例子,是 <ruby>红迪网友<rt>Redditor</rt></ruby> 的不同图片证实了:
|
||||
|
||||
![](http://www.omgubuntu.co.uk/wp-content/uploads/2017/03/cat-xfce-bug-1-750x395.jpeg)
|
||||
|
||||
目前不清楚到底是 Xfce 导致的还是猫猫。如果是后者就没希望修复了,就像便宜的 Android 手机商品(译者注:原文这里是用 cats 这个单词,是 catalogues 的缩写,原文作者也是个猫奴,#TeamCat成员)从来不从他们的 <ruby>设备制造商<rt>OEM</rt></ruby > 接收升级。
|
||||
|
||||
‘对其他 Linux 发行版的 Xfce 用户们看来还是比较棘手的’(译者注:这里在原文应该是小段落的小标题)
|
||||
|
||||
值得庆幸的是 Xubuntu 用户们并没有受到这 <ruby>爪牙<rt>clawful</rt></ruby> (译者注:clawful 是个虚构人物的名称,是<ruby>《决胜时空战区》<rt>Masters of the Universe</rt></ruby>中的邪恶战士成员) 问题的影响。这是因为它是基于 Xfce 的 Ubuntu 特色桌面发行版,能自由选老鼠的桌面壁纸。
|
||||
|
||||
|
||||
但是对其他 Linux 发行版的 Xfce 用户们看来还是比较棘手的。
|
||||
|
||||
已经提出了一个补丁修复这个问题,但是上游尚未接受。如果你们都全程关注了 bug #12117 你们可以手动应用这个补丁在你们自己的系统,去下载以下图片并设置成桌面壁纸。
|
||||
|
||||
[
|
||||
![](http://www.omgubuntu.co.uk/wp-content/uploads/2017/03/xfce-dog-wallpaper-750x363.jpg)
|
||||
][7]
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: http://www.omgubuntu.co.uk/2017/03/xfce-wallpaper-cat-bug
|
||||
|
||||
作者:[JOEY SNEDDON ][a]
|
||||
译者:[ddvio](https://github.com/ddvio)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:https://plus.google.com/117485690627814051450/?rel=author
|
||||
[1]:https://bugzilla.xfce.org/show_bug.cgi?id=12117
|
||||
[2]:https://plus.google.com/117485690627814051450/?rel=author
|
||||
[3]:http://www.omgubuntu.co.uk/category/random-2
|
||||
[4]:http://www.omgubuntu.co.uk/wp-content/uploads/2017/02/xubuntu.jpg
|
||||
[5]:http://www.omgubuntu.co.uk/2017/03/xfce-wallpaper-cat-bug
|
||||
[6]:http://www.omgubuntu.co.uk/wp-content/uploads/2017/03/cat-xfce-bug-2.jpg
|
||||
[7]:http://www.omgubuntu.co.uk/wp-content/uploads/2017/03/xfce-dog-wallpaper.jpg
|
Loading…
Reference in New Issue
Block a user