2020年5月

Twitter CEO:自动化会甚至会对编程工作构成威胁

Twitter CEO Jack Dorsey 表示,“机器学习和深度学习的许多目标是随着时间的发展编写软件本身,因此许多初级级编程工作将不再那么重要了,”

来源:新浪科技

硬核老王点评:毕竟“初级”编程工作也是要被取代的初级工种之一。

多个 DNS 解析程序漏洞允许攻击者发动拒绝服务攻击

该漏洞被称为 NXNSAttack。DNS 解析程序不能向“名字”发送域名查询,因此解析器首先需要获得权威 DNS 服务器的 IPv4 或 IPv6 地址,之后才能继续查询域名。NXNSAttack 就是基于这一原理,攻击者发送的委托包含了假的权威服务器名字,指向受害者的 DNS 服务器,迫使解析程序对受害者的 DNS 服务器生成查询。一次查询会被放大数十次乃至数百次,对受害者服务器发动了拒绝服务攻击。众多 DNS 软件都受到影响,其中包括 BIND、 Unbound、PowerDNS、Cloudflare、Google、Amazon、Microsoft、Oracle(DYN)、Verisign、IBM Quad9 和 ICANN。

来源:solidot

硬核老王点评:互联网建设和设计之初,并没有考虑到这么复杂的安全隐患,或者说考虑到了在当时也只能暂时忽视。随着互联网的发展,这种早期的协议上的漏洞会逐一被发现、修补和迭代。

GNOME 基金会和 RPI 的专利诉讼案达成和解

去年 9 月,Rothschild Patent Imaging LLC(RPI)的公司对 GNOME 基金会提起了专利侵权诉讼,指控 GNOME 桌面环境项目中的一个组件 Shotwell 照片管理器侵犯了它于 2008 年申请的专利,该专利描述了无线连接图像捕捉设备和接收设备的系统和方法。现在,GNOME 与 RPI 达成和解,RPI 并承诺不再对 GNOME 提起任何专利诉讼。此外,RPI 和 Leigh Rothschild 的专利免除和承诺覆盖了在 OSI 批准的所有开源许可证下发布的软件。

来源:solidot

硬核老王点评:妄想从开源社区/开源组织身上咬下一块肉,先要看看是否能承受社会压力。

安全研究人员分析过去几年发生的开源软件供应链攻击

软件供应链攻击有两类:其一是在软件产品中植入恶意代码去感染终端用户,此类攻击的一个例子是 CCleaner 的恶意版本通过官网传播给终端用户,它在长达一个多月时间里被下载了 230 万次。另一类软件供应链攻击是向软件产品的依赖包植入恶意代码。随着开源软件开发模式的流行,此类的攻击日益常见。研究人员分析了 npm、PyPI 和 RubyGems 软件包管理系统发现的 174 个恶意依赖包,他们发现 56% 的软件包在安装时触发恶意行为,41% 使用额外的条件去判断是否运行。61% 的恶意软件包利用了名字相似性向开源生态系统植入恶意包。攻击者的主要目的是析取数据。

来源:solidot

硬核老王点评:开源软件本身这个模式并不能决定是否安全,只是给用户一个安全的可能性。所以,在享受开源软件的福利时,其带来的可能的隐患也需要重视,并可能付出不菲的成本和代价。

微软开源 1983 年的 GW-BASIC

微软在历史参考和教育目的的名义下开源了 1983 年的 GW-BASIC,源代码托管在 GitHub 上,采用 MIT 许可证。微软表示他们不接受修改任何代码的 PR 请求。GW-BASIC 是源自 IBM Advanced BASIC/BASICA 的 BASIC 解释器。微软不同的 BASIC 实现都可以上溯到比尔盖茨和保罗艾伦完成的微软首个产品:Altair 8800 BASIC 解释器。和 70/80 年代的众多软件一样,GW-BASIC 的源代码是百分之百的汇编语言。

来源:solidot

硬核老王点评:反正放家里也是烂着,就当成古董给大家把玩吧。

Lubuntu 20.04 LTS 与之前的 LTS 版本有很大的不同。它旨在给你一个更完善的体验,而不仅仅是关注旧电脑。请阅读更多关于 Lubuntu 20.04 的内容。

Lubuntu 20.04 点评:第一个基于 LXQt 的长期支持版

我在 Lubuntu 20.04 发行前几天就已经开始使用它了。我通常使用 Arch 阵营中 Manjaro 和 Cinnamon 桌面,所以使用 Lubuntu 对我来说是一个愉快的改变。

以下是我在使用 Lubuntu 20.04.时的一些感受和注记。

再见 LXDE,你好 LXQt!

长期以来,Lubuntu 都依靠 LXDE 来提供轻量级的 Linux 体验。但现在,它使用的是 LXQt 桌面环境。

LXDE 是基于 GTK(GNOME 所使用的库),更具体地说是基于 2020 年的 GTK+ 2。由于对 GTK+ 3 不满意,LXDE 开发人员 Hong Jen Yee 决定将整个桌面移植到 Qt(KDE 所使用的库)。LXDE 的 Qt 移植版本和 Razor-qt 项目合并形成 LXQt。所以现在,LXDE 和 LXQt 作为单独的项目而共存。

既然 LXDE 开发者本身专注于 LXQt,那么 Lubuntu 坚持使用三年多前上一次稳定发布版的桌面环境 LXDE 是没有意义的。

因此,Lubuntu 18.04 是使用 LXDE 的最后一个版本。幸运的是,这是一个长期支持版本。Lubuntu 团队将提供支持直到 2021 年。

不仅适于老机器

随着在 2020 年“老机器”的定义发生了变化,Lubuntu 18.04 成为了最后一个 32 位版本。现在,即使是一台 10 年前的老机器也至少有 2G 的内存和一个双核 64 位处理器。

因此,Lubuntu 团队将不再设置最低的系统需求,也不再主要关注旧硬件。尽管 LXQt 仍然是一个轻量级的、经典而不失精致的、功能丰富的桌面环境。

在 Lubuntu 20.04 LTS 发布之前,Lubuntu 的第一个 LXQt 发行版是 18.10,开发人员经历了三个标准发行版来完善 LXQt 桌面,这是一个很好的开发策略。

不用常规的 Ubiquity,Lubuntu 20.04 使用的是 Calamares 安装程序

在新版本中使用了全新的 Calamares 安装程序,取代了其它 Ubuntu 官方版本使用的 Ubiquity 安装程序。

整个安装过程在大约能在 10 分钟内完成,比之前 Lubuntu 的版本稍微快一些。

由于镜像文件附带了预先安装的基本应用程序,所以你可以很快就可以完成系统的完全配置。

不要直接从 Lubuntu 18.04 升级到 Lubuntu 20.04

通常,你可以将 Ubuntu 从一个 LTS 版本升级到另一个 LTS 版本。但是 Lubuntu 团队建议不要从 Lubuntu 18.04 升级到 20.04。他们建议重新安装,这才是正确的。

Lubuntu 18.04 使用 LXDE 桌面,20.04 使用 LXQt。由于桌面环境的巨大变化,从 18.04 升级到 20.04 将导致系统崩溃。

更多的 KDE 和 Qt 应用程序

下面是在这个新版本中默认提供的一些应用程序,正如我们所看到的,并非所有应用程序都是轻量级的,而且大多数应用程序都是基于 Qt 的。

甚至使用的软件中心也是 KDE 的 Discover,而不是 Ubuntu 的 GNOME 软件中心。

  • Ark – 归档文件管理器
  • Bluedevil – 蓝牙连接管理
  • Discover 软件中心 – 包管理系统
  • FeatherPad – 文本编辑器
  • FireFox – 浏览器
  • K3b – CD/DVD 刻录器
  • Kcalc – 计算器
  • KDE 分区管理器 – 分区管理工具
  • LibreOffice – 办公套件(Qt 界面版本)
  • LXimage-Qt – 图片查看器及截图制作
  • Muon – 包管理器
  • Noblenote – 笔记工具
  • PCManFM-Qt – 文件管理器
  • Qlipper – 剪贴板管理工具
  • qPDFview – PDF 阅读器
  • PulseAudio – 音频控制器
  • Qtransmission – BT 下载工具(Qt 界面版本)
  • Quassel – IRC 客户端
  • ScreenGrab – 截屏制作工具
  • Skanlite – 扫描工具
  • 启动盘创建工具 – USB 启动盘制作工具
  • Trojita – 邮件客户端
  • VLC – 媒体播放器
  • MPV 视频播放器

测试 Lubuntu 20.04 LTS

LXQt 版 Lubuntu 的启动时间不到一分钟,虽然是从 SSD 启动的。

LXQt 目前需要的内存比基于 Gtk+ 2 的 LXDE 稍微多一点,但是另一种 Gtk+ 3 工具包也需要更多的内存。

在重新启动之后,系统以非常低的内存占用情况运行,大约只有 340 MB(按照现代标准),比 LXDE 多 100 MB。

LXQt 不仅适用于硬件较旧的用户,也适用于那些希望在新机器上获得简约经典体验的用户。

桌面布局看起来类似于 KDE 的 Plasma 桌面,你觉得呢?

在左下角有一个应用程序菜单,一个用于显示固定和活动的应用程序的任务栏,右下角有一个系统托盘。

Lubuntu 的 LXQt 版本可以很容易的定制,所有的东西都在菜单的首选项下,大部分的关键项目都在 LXQt “设置”中。

值得一提的是,LXQt 在默认情况下使用流行的 Openbox 窗口管理器

与前三个发行版一样,20.04 LTS 附带了一个默认的黑暗主题 Lubuntu Arc,但是如果不适合你的口味,可以快速更换,也很方便。

就日常使用而言,事实证明,Lubuntu 20.04 向我证明,其实每一个 Ubuntu 的分支版本都完全没有问题。

结论

Lubuntu 团队已经成功地过渡到一个现代的、依然轻量级的、极简的桌面环境。LXDE 看起来被遗弃了,迁移到一个活跃的项目也是一件好事。

我希望 Lubuntu 20.04 能够让你和我一样热爱,如果是这样,请在下面的评论中告诉我。请继续关注!


via: https://itsfoss.com/lubuntu-20-04-review/

作者:Dimitrios Savvopoulos 选题:lujun9972 译者:qfzy1233 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

如果你在 macOS 上运行的项目需要没有安装的 Python 版本,请试试 pyenv。

即使对于有经验的开发人员,管理本地 Python 开发环境仍然是一个挑战。尽管有详细的软件包管理策略,但仍需要采取另外的步骤来确保你在需要时运行所需的 Python 版本。

为什么 Python 版本重要?

起初这是一个奇怪的概念,但是编程语言会像其他任何软件一样发生变化。它们有错误、修复和更新,就像你喜欢的 API 和任何其他软件一样。同样,不同的发行版由称为语义化版本的三位数标识。

??? pic.twitter.com/yt1Z2439W8

— Denny Perez (@dennyperez18) May 28, 2019

多年来,Python 2 是该语言的常用主要版本。在 2020 年 1 月,Python 2 到达最后寿命,此后,Python 的核心维护者将仅支持 Python 3。Python 3 稳步发展,并定期发布新更新。对我来说定期获取这些更新很重要。

最近,我试图在 macOS 上运行一个依赖于 Python 3.5.9 的项目,而我的系统上并没有安装这个版本。我认为 Python 包管理器 pip 可以安装它,但事实并非如此:

$ pip install python3.5.9
Collecting python3.5.9
  ERROR: Could not find a version that satisfies the requirement python3.5.9 (from versions: none)
ERROR: No matching distribution found for python3.5.9

或者,我也可以从官方 Python 网站下载该版本,但我如何在我的 Mac 上与现有的 Python 版本一起运行?每次运行时指定 Python 解释器版本(例如 python3.7 或 python3.5)似乎很容易出错。一定会有更好的方法。

(说明:我知道这对经验丰富的 Python 开发人员没有意义,但对当时的我来说是有意义的。我很乐意谈一谈为什么我仍然认为它应该这样做。)

安装和设置 pyenv

值得庆幸的是,pyenv 可以绕开这一系列复杂的问题。首先,我需要安装 pyenv。我可以从源码克隆并编译它,但是我更喜欢通过 Homebrew 包管理器来管理软件包:

$ brew install pyenv

为了通过 pyenv 使用 Python 版本,必须了解 shell 的 PATH 变量。PATH 决定了 shell 通过命令的名称来搜索文件的位置。你必须确保 shell 程序能够找到通过 pyenv 运行的 Python 版本,而不是默认安装的版本(通常称为系统版本)。如果不更改路径,那么结果如下:

$ which python
/usr/bin/python

这是 Python 的系统版本。

要正确设置 pyenv,可以在 Bash 或 zsh 中运行以下命令:

$ PATH=$(pyenv root)/shims:$PATH

现在,如果你检查 Python 的版本,你会看到它是 pyenv 管理的版本:

$ which python
/Users/my_username/.pyenv/shims/python

该导出语句(PATH=)仅会对该 shell 实例进行更改,为了使更改永久生效,你需要将它添加到点文件当中。由于 zsh 是 macOS 的默认 shell,因此我将重点介绍它。将相同的语法添加到 ~/.zshrc 文件中:

$ echo 'PATH=$(pyenv root)/shims:$PATH' >> ~/.zshrc

现在,每次我们在 zsh 中运行命令时,它将使用 pyenv 版本的 Python。请注意,我在 echo 中使用了单引号,因此它不会评估和扩展命令。

.zshrc 文件仅管理 zsh 实例,因此请确保检查你的 shell 程序并编辑关联的点文件。如果需要再次检查默认 shell 程序,可以运行 echo $SHELL。如果是 zsh,请使用上面的命令。如果你使用 Bash,请将 ~/.zshrc 更改为 ~/.bashrc。如果你想了解更多信息,可以在 pyenvREADME 中深入研究路径设置

使用 pyenv 管理 Python 版本

现在 pyenv 已经可用,我们可以看到它只有系统 Python 可用:

$ pyenv versions
system

如上所述,你绝对不想使用此版本(阅读更多有关信息)。现在 pyenv 已正确设置,我希望它能有我经常使用的几个不同版本的 Python。

有一种方法可以通过运行 pyenv install --list 来查看 pyenv 可以访问的所有仓库中的所有 Python 版本。这是一个很长的列表,将来回顾的时候可能会有所帮助。目前,我决定在 Python 下载页面找到的每个最新的“点版本”(3.5.x 或 3.6.x,其中 x 是最新的)。因此,我将安装 3.5.9 和 3.8.0:

$ pyenv install 3.5.9
$ pyenv install 3.8.0

这将需要一段时间,因此休息一会(或阅读上面的链接之一)。有趣的是,输出中显示了该版本的 Python 的下载和构建。例如,输出显示文件直接来自 Python.org

安装完成后,你可以设置默认值。我喜欢最新的,因此将全局默认 Python 版本设置为最新版本:

$ pyenv global 3.8.0

该版本立即在我的 shell 中设置完成。确认一下:

$ python -V
Python 3.8.0

我要运行的项目仅适于 Python 3.5,因此我将在本地设置该版本并确认:

$ pyenv local 3.5.9
$ python -V
Python 3.5.9

因为我在 pyenv 中使用了 local 选项,所以它向当前目录添加了一个文件来跟踪该信息。

$ cat .python-version
3.5.9

现在,我终于可以为想要的项目设置虚拟环境,并确保运行正确版本的 Python。

$ python -m venv venv
$ source ./venv/bin/activate
(venv) $ which python
/Users/mbbroberg/Develop/my_project/venv/bin/python

要了解更多信息,请查看有关在 Mac 上管理虚拟环境的教程。

总结

默认情况下,运行多个 Python 版本可能是一个挑战。我发现 pyenv 可以确保在我需要时可以有我需要的 Python 版本。

你还有其他初学者或中级 Python 问题吗? 请发表评论,我们将在以后的文章中考虑介绍它们。


via: https://opensource.com/article/20/4/pyenv

作者:Matthew Broberg 选题:lujun9972 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

初代 Xbox 主机操作系统和 Windows NT 3.5 源码泄露

据外媒 The Verge 的报道,微软初代 Xbox 游戏主机操作系统和 Windows NT 3.5 的源码已被泄露,在本月早些时候被泄露的代码已出现在互联网上。前者被泄露的代码包括初代 Xbox 主机操作系统的部分内容——操作系统内核(Windows 2000 的定制版本)。此外,Windows NT 3.5 接近最终版本的源码也出现在了互联网上。源代码包括了所有必要的构建工具,由于对 Windows NT 3.5 的支持已在 2001 年 12 月结束,而且该内核只在全球范围内的少数系统中使用,相信不会造成严重的安全问题。

来源:开源中国

硬核老王点评:即便以现在的眼光,当年的 Windows NT 3.5 依然很棒。

性能提升 10 倍,蚂蚁金服的 OceanBase 二次 TPC-C 测试结果公布

时隔半年,蚂蚁金服的自研数据库 OceanBase 又去做了 TPC-C 测试。TPC 去年 10 月 2 日和今年 5 月 20 日公布的测试结果显示,OceanBase 都是榜单第一。性能方面,去年 OceanBase 的测试结果是 6088万 tpmC,今年则是 7.07亿 tpmC;TPC-C benchmark 价格也从 ¥6.25/tpmC 降低到 ¥3.98/tpmC。不过,一些数据库公司,如 Oracle 并未参与这两次测试。之所以在短时间内做两次测试,是因为 OceanBase 团队认为首次测试尚未充分发挥 OceanBase 分布式架构的真正实力。

来源:开源中国

硬核老王点评:之前有人质疑 OceanBase 取得高评分是由于硬件的进步,所以这次重新参加测试应该是来证明其本身软件的提升。

cd 命令可能是任何 Linux 用户学习的前 10 个命令之一,但这并不是在 Linux 文件系统中导航的唯一方法,这里还有其他一些方法。

无论你是在文件系统中四处查看、寻找文件还是尝试进入重要目录,Linux 都可以提供很多帮助。在本文中,我们将介绍一些技巧,使你可以在文件系统中移动,查找和使用所需的命令也更加轻松。

添加到 $PATH

确保你不必花费大量时间在 Linux 系统上查找命令的最简单、最有用的方法之一就是在 $PATH 变量中添加适当的目录。但是,添加到 $PATH 变量中的目录顺序非常重要。它们确定系统在目录中查找要运行命令的目录顺序–在找到第一个匹配项时停止。

例如,你可能希望将家目录放在第一个,这样,如果你创建的脚本与其他可执行文件有相同的名称,那么只要输入该脚本的名称,它便会运行。

要将家目录添加到 $PATH 变量中,可以执行以下操作:

$ export PATH=~:$PATH

~ 字符代表家目录。

如果将脚本保存在 bin 目录中,下面的会有效:

$ export PATH=~/bin:$PATH

然后,你可以运行位于家目录中的脚本,如下所示:

$ myscript
Good morning, you just ran /home/myacct/bin/myscript

重要提示:上面显示的命令会添加到你的搜索路径中,因为 $PATH(当前路径)被包含在内。它们不会覆盖它。你的搜索路径应该在你的 .bashrc 文件中配置,任何你打算永久化的更改也应该添加到那里。

使用符号链接

符号链接提供了一种简单而明显的方式来记录可能经常需要使用的目录的位置。例如,如果你管理网站的内容,那么可能需要通过创建如下链接来使你的帐户“记住”网页文件的位置:

ln -s /var/www/html www

参数的顺序很重要。第一个(/var/www/html)是目标,第二个是你创建的链接的名称。如果你当前不在家目录中,那么以下命令将执行相同的操作:

ln -s /var/www/html ~/www

设置好之后,你可以使用 cd www 进入 /var/www/html

使用 shopt

shopt 命令还提供了一种让移动到其他目录更加容易的方法。当你使用 shoptautocd 选项时,只需输入名称即可转到目录。例如:

$ shopt -s autocd
$ www
cd -- www
/home/myacct/www
$ pwd -P
/var/www/html

$ ~/bin
cd -- /home/myacct/bin
$ pwd
/home/myacct/bin

在上面的第一组命令中,启用了 shopt 命令的 autocd 选项。输入 www,就会调用 cd www 命令。由于此符号链接是在上面的 ln 命令示例之一中创建的,因此将我们移至 /var/www/htmlpwd -P 命令显示实际位置。

在第二组中,键入 ~/bin 会调用 cd 进入在用户家目录的 bin 目录。

请注意,当你输入的是命令时,autocd 行为将不会生效,即使它也是目录的名称。

shopt 是 bash 内置命令,它有很多选项。这只是意味着你不必在要进入每个目录的名称之前输入 cd

要查看 shopt 的其他选项,只需输入 shopt

使用 $CDPATH

可能进入特定目录的最有用技巧之一,就是将你希望能够轻松进入的路径添加到 $CDPATH 中。这将创建一个目录列表,只需输入完整路径名的一部分即可进入。

一方面,这可能有点棘手。你的 $CDPATH 需要包含要移动到的目录的父目录,而不是目录本身。

例如,假设你希望仅通过输入 cd html 就可以移至 /var/www/html 目录,并仅使用 cd 和简单目录名即可移至 /var/log 中的子目录。在这种情况下,此 $CDPATH 就可以起作用:

$ CDPATH=.:/var/log:/var/www

你将看到:

$ cd journal
/var/log/journal
$ cd html
/var/www/html

当你输入的不是完整路径时,$CDPATH 就会生效。它向下查看其目录列表,以查看指定的目录是否存在于其中一个目录中。找到匹配项后,它将带你到那里。

$CDPATH 开头保持 . 意味着你可以进入本地目录,而不必在 $CDPATH 中定义它们。

$ export CDPATH=".:$CDPATH"
$ Videos
cd -- Videos
/home/myacct/Videos

在 Linux 文件系统键切换并不难,但是如果你使用一些方便的技巧轻松地到达各个位置,那你可以节省一些大脑细胞。


via: https://www.networkworld.com/article/3533421/tricks-for-getting-around-your-linux-file-system.html

作者:Sandra Henry-Stocker 选题:lujun9972 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

大家常规的认知是,Go 程序中声明的类型越多,生成的二进制文件就越大。这个符合直觉,毕竟如果你写的代码不去操作定义的类型,那么定义一堆类型就没有意义了。然而,链接器的部分工作就是检测没有被程序引用的函数(比如说它们是一个库的一部分,其中只有一个子集的功能被使用),然后把它们从最后的编译产出中删除。常言道,“类型越多,二进制文件越大”,对于多数 Go 程序还是正确的。

本文中我会深入讲解在 Go 程序的上下文中“相等”的意义,以及为什么像这样的修改会对 Go 程序的大小有重大的影响。

定义两个值相等

Go 的语法定义了“赋值”和“相等”的概念。赋值是把一个值赋给一个标识符的行为。并不是所有声明的标识符都可以被赋值,如常量和函数就不可以。相等是通过检查标识符的内容是否相等来比较两个标识符的行为。

作为强类型语言,“相同”的概念从根源上被植入标识符的类型中。两个标识符只有是相同类型的前提下,才有可能相同。除此之外,值的类型定义了如何比较该类型的两个值。

例如,整型是用算数方法进行比较的。对于指针类型,是否相等是指它们指向的地址是否相同。映射和通道等引用类型,跟指针类似,如果它们指向相同的地址,那么就认为它们是相同的。

上面都是按位比较相等的例子,即值占用的内存的位模式是相同的,那么这些值就相等。这就是所谓的 memcmp,即内存比较,相等是通过比较两个内存区域的内容来定义的。

记住这个思路,我过会儿再来谈。

结构体相等

除了整型、浮点型和指针等标量类型,还有复合类型:结构体。所有的结构体以程序中的顺序被排列在内存中。因此下面这个声明:

type S struct {
    a, b, c, d int64
}

会占用 32 字节的内存空间;a 占用 8 个字节,b 占用 8 个字节,以此类推。Go 的规则说如果结构体所有的字段都是可以比较的,那么结构体的值就是可以比较的。因此如果两个结构体所有的字段都相等,那么它们就相等。

a := S{1, 2, 3, 4}
b := S{1, 2, 3, 4}
fmt.Println(a == b) // 输出 true

编译器在底层使用 memcmp 来比较 a 的 32 个字节和 b 的 32 个字节。

填充和对齐

然而,在下面的场景下过分简单化的按位比较的策略会返回错误的结果:

type S struct {
    a byte
    b uint64
    c int16
    d uint32
}

func main()
    a := S{1, 2, 3, 4}
    b := S{1, 2, 3, 4}
    fmt.Println(a == b) // 输出 true
}

编译代码后,这个比较表达式的结果还是 true,但是编译器在底层并不能仅依赖比较 ab 的位模式,因为结构体有填充

Go 要求结构体的所有字段都对齐。2 字节的值必须从偶数地址开始,4 字节的值必须从 4 的倍数地址开始,以此类推 1 。编译器根据字段的类型和底层平台加入了填充来确保字段都对齐。在填充之后,编译器实际上看到的是 2

type S struct {
    a byte
    _ [7]byte // 填充
    b uint64
    c int16
    _ [2]int16 // 填充
    d uint32
}

填充的存在保证了字段正确对齐,而填充确实占用了内存空间,但是填充字节的内容是未知的。你可能会认为在 Go 中 填充字节都是 0,但实际上并不是 — 填充字节的内容是未定义的。由于它们并不是被定义为某个确定的值,因此按位比较会因为分布在 s 的 24 字节中的 9 个填充字节不一样而返回错误结果。

Go 通过生成所谓的相等函数来解决这个问题。在这个例子中,s 的相等函数只比较函数中的字段略过填充部分,这样就能正确比较类型 s 的两个值。

类型算法

呵,这是个很大的设置,说明了为什么,对于 Go 程序中定义的每种类型,编译器都会生成几个支持函数,编译器内部把它们称作类型的算法。如果类型是一个映射的键,那么除相等函数外,编译器还会生成一个哈希函数。为了维持稳定,哈希函数在计算结果时也会像相等函数一样考虑诸如填充等因素。

凭直觉判断编译器什么时候生成这些函数实际上很难,有时并不明显,(因为)这超出了你的预期,而且链接器也很难消除没有被使用的函数,因为反射往往导致链接器在裁剪类型时变得更保守。

通过禁止比较来减小二进制文件的大小

现在,我们来解释一下 Brad 的修改。向类型添加一个不可比较的字段 3 ,结构体也随之变成不可比较的,从而强制编译器不再生成相等函数和哈希函数,规避了链接器对那些类型的消除,在实际应用中减小了生成的二进制文件的大小。作为这项技术的一个例子,下面的程序:

package main

import "fmt"

func main() {
    type t struct {
        // _ [0][]byte // 取消注释以阻止比较
        a byte
        b uint16
        c int32
        d uint64
    }
    var a t
    fmt.Println(a)
}

用 Go 1.14.2(darwin/amd64)编译,大小从 2174088 降到了 2174056,节省了 32 字节。单独看节省的这 32 字节似乎微不足道,但是考虑到你的程序中每个类型及其传递闭包都会生成相等和哈希函数,还有它们的依赖,这些函数的大小随类型大小和复杂度的不同而不同,禁止它们会大大减小最终的二进制文件的大小,效果比之前使用 -ldflags="-s -w" 还要好。

最后总结一下,如果你不想把类型定义为可比较的,可以在源码层级强制实现像这样的奇技淫巧,会使生成的二进制文件变小。


附录:在 Brad 的推动下,Cherry ZhangKeith Randall 已经在 Go 1.15 做了大量的改进,修复了最严重的故障,消除了无用的相等和哈希函数(虽然我猜想这也是为了避免这类 CL 的扩散)。

相关文章:

  1. Go 运行时如何高效地实现映射(不使用泛型)")
  2. 空结构体
  3. 填充很难
  4. Go 中有类型的 nil(2)

  1. 在 32 位平台上 int64unit64 的值可能不是按 8 字节对齐的,因为平台原生的是以 4 字节对齐的。查看 议题 599 了解内部详细信息。
  2. 32 位平台会在 ab 的声明中填充 _ [3]byte。参见前一条。
  3. Brad 使用的是[0]func(),但是所有能限制和禁止比较的类型都可以。添加了一个有 0 个元素的数组的声明后,结构体的大小和对齐不会受影响。

via: https://dave.cheney.net/2020/05/09/ensmallening-go-binaries-by-prohibiting-comparisons

作者:Dave Cheney 选题:lujun9972 译者:lxbwolf 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出