2018年2月

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

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

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

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

保持地址跟踪

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

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

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

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

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

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

一半一半

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

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

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

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

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

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

Ring 间迁移

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

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

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

这个问题第一次被披露是由来自 奥地利的 Graz Technical University 的研究者。他们披露的信息表明这个问题已经足够破坏内核模式地址空间布局随机化(内核 ASLR,或称 KASLR)。ASLR 是防范 缓冲区溢出 漏洞利用的最后一道防线。启用 ASLR 之后,程序和它们的数据被置于随机的内存地址中,它将使一些安全漏洞利用更加困难。KASLR 将这种随机化应用到内核中,这样就使内核的数据(包括页面表)和代码也随机化分布。

Graz 的研究者开发了 KAISER,一组防范这个问题的 Linux 内核补丁。

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

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

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

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

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

但是大量调用进入到内核的工作负载将观测到很大的性能损失。在一个基准测试中,一个除了调入到内核之外什么都不做的程序,观察到 它的性能下降大约为 50%;换句话说就是,打补丁后每次对内核的调用的时间要比不打补丁调用内核的时间增加一倍。基准测试使用的 Linux 的网络回环(loopback)也观测到一个很大的影响,比如,在 Postgres 的基准测试中大约是 17%。真实的数据库负载使用了实时网络可能观测到的影响要低一些,因为使用实时网络时,内核调用的开销基本是使用真实网络的开销。

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


PETER BRIGHT 是 Ars 的一位技术编辑。他涉及微软、编程及软件开发、Web 技术和浏览器、以及安全方面。它居住在纽约的布鲁克林。


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

作者:PETER BRIGHT 译者:qhwdw 校对:wxy

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

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

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

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

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

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

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

uBlock Origin

ublock origin ad blocker screenshot

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

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

Privacy Badger

privacy badger ad blocker

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

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

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

LastPass

lastpass password manager screenshot

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

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

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

Xmarks Sync

Xmarks Sync 是一款方便的插件,能跨实例同步你的书签、打开的标签页、配置项和浏览器历史。如果你有多台机器,想要在桌面设备和移动设备之间同步、或者在同一台设备使用不同的浏览器,那来看看 Xmarks Sync 。(注意这款插件最近被 LastPass 收购)

Awesome Screenshot Plus

Awesome Screenshot Plus 允许你很容易捕获任意网页的全部或部分区域,也能添加注释、评论、使敏感信息模糊等。你还能用一个可选的在线服务来分享图片。我发现这工具在网页调试时截图、讨论设计和分享信息上很棒。这是一款比你预期中发现自己使用得多的工具。

Awesome Screenshot Plus screenshot

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

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


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

作者:Jeremy Garcia 译者:ypingcn 校对:wxy

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

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

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

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

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

关于代码

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

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

如果您已经在一个 IDE 或文本编辑器中敲击了几个小时,那么您可能需要学习更多关于编码的不同方法。Fraser Tweedale 的函数式编程简介很好地介绍了可以应用到许多广泛使用的编程语言的范式。

踏足 Linux

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

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

Jason Baker 告诉你设置 Linux $PATH 变量。他引导你掌握这一“任何 Linux 初学者的重要技巧”,使您能够告知系统包含了程序和脚本的目录。

感谢 David Both 的建立一个 DNS 域名服务器指南。他详细地记录了如何设置和运行服务器,包括要编辑的配置文件以及如何编辑它们。

想在你的电脑上更复古一点吗?Jim Hall 告诉你如何使用 FreeDOSqemu 在 Linux 下运行 DOS 程序。Hall 的文章着重于运行 DOS 生产力工具,但并不全是严肃的——他也谈到了运行他最喜欢的 DOS 游戏。

3 片(篇)树莓派

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

其中,树莓派可能是最广泛使用的单板计算机。Ben Nuttall 带我们一起在树莓派上安装和设置 Postgres 数据库。这样,你可以在任何你想要的项目中使用它。

如果你的品味包括文学和技术,你可能会对 Don Watkins 的如何将树莓派变成电子书服务器感兴趣。稍微付出一点努力和一份 Calibre 电子书管理软件副本,你就可以得到你最喜欢的电子书,无论你在哪里。

树莓派并不是其中唯一有特点的。还有 Orange Pi Pc Plus,这是一种开源的单板机。David Egts 告诉你如何开始使用这个可编程的迷你电脑

日常的计算机使用

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

当你想到微博客的时候,你可能会想到 Twitter。但是 Twitter 的问题很多。Mastodon 是 Twitter 的开放的替代方案,它在 2016 年首次亮相。从此, Mastodon 就获得相当大的用户基数。Seth Kenlon 说明如何加入和使用 Mastodon,甚至告诉你如何在 Mastodon 和 Twitter 间交替使用。

你需要一点帮助来维持开支吗?你所需要的只是一个电子表格和正确的模板。我关于要控制你的财政状况的文章,向你展示了如何用 LibreOffice Calc (或任何其他电子表格编辑器)创建一个简单而有吸引力的财务跟踪。

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

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


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

作者:Scott Nesbitt 译者:zjon 校对:wxy

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

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

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

那么 lftp 的独特之处是什么?

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

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

lftppget 命令。它能让你并行下载。语法是:

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

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

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

示例输出:

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

这里:

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

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

再尝试添加 exit 命令:

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

关于并行下载的说明

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

其它的命令提供了更多功能。有关更多信息,请参考 lftp 的 man 页面:

man lftp

关于作者

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


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

作者:Vivek Gite 译者:geekpi 校对:wxy

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

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

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

Linux whereis 命令

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

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

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

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

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

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

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

whereis whereis

How to find location of binary file using whereis

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

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

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

whereis -b cp

How to specifically search for binaries, manuals, or source code

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

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

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

whereis -B /usr/bin/ -f cp

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

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

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

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

whereis -l

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

How to see paths that whereis uses for search

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

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

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

whereis -m -u *

总结

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


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

作者:Himanshu Arora 译者:wenwensnow 校对:wxy

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

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

之前的文章介绍了 ncurses 库,并提供了一个简单的程序展示了一些将文本放到屏幕上的 curses 函数。在接下来的文章中,我将介绍如何使用其它的 curses 函数。

探险

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

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

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

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

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

构建程序

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

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

在这个程序,我添加了另外的语句:

keypad(stdscr, TRUE);

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

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

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

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

void draw_map(void)
{
    int y, x;

    /* 绘制探索地图 */

    /* 背景 */

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

    /* 山和山道 */

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

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

    /* 湖 */

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

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

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

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

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

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

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

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

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


    do {
        ch = getch();

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

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

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

/* quest.c */

#include 
#include 

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

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

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

    /* 初始化curses */

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

    clear();

    /* 初始化探索地图 */

    draw_map();

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

    y = LINES - 1;
    x = 0;

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

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

    ch = getch();

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

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

    endwin();

    exit(0);
}

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

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

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

void draw_map(void)
{
    int y, x;

    /* 绘制探索地图 */

    /* 背景 */

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

    /* 山脉和山道 */

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

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

    /* 湖 */

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

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

  1. 初始化 curses 环境。
  2. 绘制地图。
  3. 初始化玩家坐标(左下角)
  4. 循环:

    • 绘制玩家的角色。
    • 从键盘获取键值。
    • 对应地上下左右调整玩家坐标。
    • 重复。
  5. 完成时关闭curses环境并退出。

开始玩

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

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

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

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

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

下一步

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


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

作者:Jim Hall 译者:Leemeans 校对:wxy

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