2018年12月

手册页(man)是由系统管理员和 IT 技术开发人员写的,更多的是为了作为参考而不是教你如何使用。手册页对于已经熟悉使用 Linux、Unix 和 BSD 操作系统的人来说是非常有用的。如果你仅仅需要知道某个命令或者某个配置文件的格式那么你可以使用手册页,但是手册页对于 Linux 新手来说并没有太大的帮助。想要通过使用手册页来学习一些新东西不是一个好的选择。这里有将提供 30 个学习 Linux 和 Unix 操作系统的最佳在线网页文档。

 title=

值得一提的是,相对于 Linux,BSD 的手册页更好。

1:Red Hat Enterprise Linux(RHEL)

 title=

RHEL 是由红帽公司开发的面向商业市场的 Linux 发行版。红帽的文档是最好的文档之一,涵盖从 RHEL 的基础到一些高级主题比如安全、SELinux、虚拟化、目录服务器、服务器集群、JBOSS 应用程序服务器、高性能计算(HPC)等。红帽的文档已经被翻译成 22 种语言,发布成多页面 HTML、单页面 HTML、PDF、EPUB 等文件格式。好消息同样的文档你可以用于 Centos 和 Scientific Linux(社区企业发行版)。这些文档随操作系统一起下载提供,也就是说当你没有网络的时候,你也可以使用它们。RHEL 的文档涵盖从安装到配置器群的所有内容。唯一的缺点是你需要成为付费用户。当然这对于企业公司来说是一件完美的事。

  1. RHEL 文档:HTML/PDF格式(LCTT 译注:此链接需要付费用户才可以访问)
  2. 是否支持论坛:只能通过红帽公司的用户网站提交支持案例。

关于 CentOS Wiki 和论坛的说明

 title=

CentOS( 社区企业操作系统 Community ENTerprise Operating System )是由 RHEL 提供的自由源码包免费重建的。它为个人电脑或其它用途提供了可靠的、免费的企业级 Linux。你可以不用付出任何支持和认证费用就可以获得 RHEL 的稳定性。CentOS的 wiki 分为 Howto、技巧等等部分,链接如下:

  1. 文档:wiki 格式
  2. 是否支持论坛:

2:Arch 的 Wiki 和论坛

 title=

Arch linux 是一个独立开发的 Linux 操作系统,它有基于 wiki 网站形式的非常不错的文档。它是由 Arch 社区的一些用户共同协作开发出来的,并且允许任何用户添加或修改内容。这些文档教程被分为几类比如说优化、软件包管理、系统管理、X window 系统还有获取安装 Arch Linux 等。它的官方论坛在解决许多问题的时候也非常有用。它有总共 4 万多个注册用户、超过 1 百万个帖子。 该 wiki 包含一些 其它 Linux 发行版也适用的通用信息

  1. Arch 社区文档:Wiki 格式
  2. 是否支持论坛:

3:Gentoo Linux Wiki 和论坛

 title=

Gentoo Linux 基于 Portage 包管理系统。Gentoo Linux 用户根据它们选择的配置在本地编译源代码。多数 Gentoo Linux 用户都会定制自己独有的程序集。 Gentoo Linux 的文档会给你一些有关 Gentoo Linux 操作系统的说明和一些有关安装、软件包、网络和其它等主要出现的问题的解决方法。Gentoo 有对你来说 非常有用的论坛,论坛中有超过 13 万 4 千的用户,总共发了有 5442416 个文章。

  1. Gentoo 社区文档:手册Wiki 格式
  2. 是否支持论坛:

4:Ubuntu Wiki 和文档

 title=

Ubuntu 是领先的台式机和笔记本电脑发行版之一。其官方文档由 Ubuntu 文档工程开发维护。你可以在从官方文档中查看大量的信息,比如如何开始使用 Ubuntu 的教程。最好的是,此处包含的这些信息也可用于基于 Debian 的其它系统。你可能会找到由 Ubuntu 的用户们创建的社区文档,这是一份有关 Ubuntu 的使用教程和技巧等。Ubuntu Linux 有着网络上最大的 Linux 社区的操作系统,它对新用户和有经验的用户均有助益。

  1. Ubuntu 社区文档:wiki 格式
  2. Ubuntu 官方文档:wiki 格式
  3. 是否支持论坛:

5:IBM Developer Works

 title=

IBM Developer Works 为 Linux 程序员和系统管理员提供技术资源,其中包含数以百计的文章、教程和技巧来协助 Linux 程序员的编程工作和应用开发还有系统管理员的日常工作。

  1. IBM 开发者项目文档:HTML 格式
  2. 是否支持论坛:

6:FreeBSD 文档和手册

 title=

FreeBSD 的手册是由 FreeBSD 文档项目 FreeBSD Documentation Project 所创建的,它介绍了 FreeBSD 操作系统的安装、管理和一些日常使用技巧等内容。FreeBSD 的手册页通常比 GNU Linux 的手册页要好一点。FreeBSD 附带有全部最新手册页的文档。 FreeBSD 手册涵盖任何你想要的内容。手册包含一些通用的 Unix 资料,这些资料同样适用于其它的 Linux 发行版。FreeBSD 官方论坛会在你遇到棘手问题时给予帮助。

  1. FreeBSD 文档:HTML/PDF 格式
  2. 是否支持论坛:

7:Bash Hackers Wiki

 title=

这是一个对于 bash 使用者来说非常好的资源。Bash 使用者的 wiki 是为了归纳所有类型的 GNU Bash 文档。这个项目的动力是为了提供可阅读的文档和资料来避免用户被迫一点一点阅读 Bash 的手册,有时候这是非常麻烦的。Bash Hackers Wiki 分为各个类,比如说脚本和通用资料、如何使用、代码风格、bash 命令格式和其它。

  1. Bash 用户教程:wiki 格式

8:Bash 常见问题

 title=

这是一个为 bash 新手设计的一个 wiki。它收集了 IRC 网络的 #bash 频道里常见问题的解决方法,这些解决方法是由该频道的普通成员提供。当你遇到问题的时候不要忘了在 BashPitfalls 部分检索查找答案。这些常见问题的解决方法可能会倾向于 Bash,或者偏向于最基本的 Bourne Shell,这决定于是谁给出的答案。大多数情况会尽力提供可移植的(Bourne)和高效的(Bash,在适当情况下)的两类答案。

  1. Bash 常见问题:wiki 格式

9: Howtoforge - Linux 教程

 title=

博客作者 Falko 在 Howtoforge 上有一些非常不错的东西。这个网站提供了 Linux 关于各种各样主题的教程,比如说其著名的“最佳服务器系列”,网站将主题分为几类,比如说 web 服务器、linux 发行版、DNS 服务器、虚拟化、高可用性、电子邮件和反垃圾邮件、FTP 服务器、编程主题还有一些其它的内容。这个网站也支持德语。

  1. Howtoforge: html 格式
  2. 是否支持论坛:是

10:OpenBSD 常见问题和文档

 title=

OpenBSD 是另一个基于 BSD 的类 Unix 计算机操作系统。OpenBSD 是由 NetBSD 项目分支而来。OpenBSD 因高质量的代码和文档、对软件许可协议的坚定立场和强烈关注安全问题而闻名。OpenBSD 的文档分为多个主题类别,比如说安装、包管理、防火墙设置、用户管理、网络、磁盘和磁盘阵列管理等。

  1. OpenBSD:html 格式
  2. 是否支持论坛:否,但是可以通过 邮件列表 来咨询

11: Calomel - 开源研究和参考文档

 title=

这个极好的网站是专门作为开源软件和那些特别专注于 OpenBSD 的软件的文档来使用的。这是最简洁的引导网站之一,专注于高质量的内容。网站内容分为多个类,比如说 DNS、OpenBSD、安全、web 服务器、Samba 文件服务器、各种工具等。

  1. Calomel 官网:html 格式
  2. 是否支持论坛:否

12:Slackware 书籍项目

 title=

Slackware Linux 是我的第一个 Linux 发行版。Slackware 是基于 Linux 内核的最早的发行版之一,也是当前正在维护的最古老的 Linux 发行版。 这个发行版面向专注于稳定性的高级用户。 Slackware 也是很少有的的“类 Unix” 的 Linux 发行版之一。官方的 Slackware 手册是为了让用户快速开始了解 Slackware 操作系统的使用方法而设计的。 这不是说它将包含发行版的每一个方面,而是为了说明它的实用性和给使用者一些有关系统的基础工作使用方法。手册分为多个主题,比如说安装、网络和系统配置、系统管理、包管理等。

  1. Slackware Linux 手册:html 格式、pdf 和其它格式
  2. 是否支持论坛:是

13:Linux 文档项目(TLDP)

 title=

Linux 文档项目 Linux Documentation Project 旨在给 Linux 操作系统提供自由、高质量文档。网站是由志愿者创建和维护的。网站分为具体主题的帮助、由浅入深的指南等。在此我想推荐一个非常好的文档,这个文档既是一个教程也是一个 shell 脚本编程的参考文档,对于新用户来说这个 HOWTO 的列表也是一个不错的开始。

  1. Linux 文档工程 支持多种查阅格式
  2. 是否支持论坛:否

14:Linux Home Networking

 title=

Linux Home Networking 是学习 linux 的另一个比较好的资源,这个网站包含了 Linux 软件认证考试的内容比如 RHCE,还有一些计算机培训课程。网站包含了许多主题,比如说网络、Samba 文件服务器、无线网络、web 服务器等。

  1. Linux home networking 可通过 html 格式和 PDF(少量费用)格式查阅
  2. 是否支持论坛:是

15:Linux Action Show

 title=

Linux Action Show(LAS) 是一个关于 Linux 的播客。这个网站是由 Bryan Lunduke、Allan Jude 和 Chris Fisher 共同管理的。它包含了 FOSS 的最新消息。网站内容主要是评论一些应用程序和 Linux 发行版。有时候也会发布一些和开源项目著名人物的采访视频。

  1. Linux action show 支持音频和视频格式
  2. 是否支持论坛:是

16:Commandlinefu

 title=

Commandlinefu 列出了各种有用或有趣的 shell 命令。这里所有命令都可以评论、讨论和投票(支持或反对)。对于所有 Unix 命令行用户来说是一个极好的资源。不要忘了查看评选出来的最佳命令

  1. Commandlinefu 支持 html 格式
  2. 是否支持论坛:否

17:Debian 管理技巧和资源

 title=

这个网站包含一些只和 Debian GNU/Linux 相关的主题、技巧和教程,特别是包含了关于系统管理的有趣和有用的信息。你可以在上面贡献文章、建议和问题。提交了之后不要忘记查看最佳文章列表里有没有你的文章。

  1. Debian 系统管理 支持 html 格式
  2. 是否支持论坛:否

18: Catonmat - Sed、Awk、Perl 教程

 title=

这个网站是由博客作者 Peteris Krumins 维护的。主要关注命令行和 Unix 编程主题,比如说 sed 流编辑器、perl 语言、AWK 文本处理工具等。不要忘了查看 sed 介绍、sed 含义解释,还有命令行历史的权威介绍

  1. catonmat 支持 html 格式
  2. 是否支持论坛:否

19:Debian GNU/Linux 文档和 Wiki

 title=

Debian 是另外一个 Linux 操作系统,其主要使用的软件以 GNU 许可证发布。Debian 因严格坚持 Unix 和自由软件的理念而闻名,它也是很受欢迎并且有一定影响力的 Linux 发行版本之一。 Ubuntu 等发行版本都是基于 Debian 的。Debian 项目以一种易于访问的形式提供给用户合适的文档。这个网站分为 Wiki、安装指导、常见问题、支持论坛几个模块。

  1. Debian GNU/Linux 文档 支持 html 和其它格式访问
  2. Debian GNU/Linux wiki
  3. 是否支持论坛:

20:Linux Sea

Linux Sea 这本书提供了比较通俗易懂但充满技术(从最终用户角度来看)的 Linux 操作系统的介绍,使用 Gentoo Linux 作为例子。它既没有谈论 Linux 内核或 Linux 发行版的历史,也没有谈到 Linux 用户不那么感兴趣的细节。

  1. Linux sea 支持 html 格式访问
  2. 是否支持论坛: 否

21:O'reilly Commons

 title=

O'reilly 出版社发布了不少 wiki 格式的文章。这个网站主要是为了给那些喜欢创作、参考、使用、修改、更新和修订来自 O'Reilly 或者其它来源的素材的社区提供资料。这个网站包含关于 Ubuntu、PHP、Spamassassin、Linux 等的免费书籍。

  1. Oreilly commons 支持 Wiki 格式
  2. 是否支持论坛:否

22:Ubuntu 袖珍指南

 title=

这本书的作者是 Keir Thomas。这本指南(或者说是书籍)对于所有 ubuntu 用户来说都值得一读。这本书旨在向用户介绍 Ubuntu 操作系统和其所依赖的理念。你可以从官网下载这本书的 PDF 版本,也可以在亚马逊买印刷版。

  1. Ubuntu pocket guide 支持 PDF 和印刷版本.
  2. 是否支持论坛:否

23: Linux: Rute User’s Tutorial and Exposition

 title=

这本书涵盖了 GNU/LINUX 系统管理,主要是对主流的发布版本比如红帽和 Debian 的说明,可以作为新用户的教程和高级管理员的参考。这本书旨在给出 Unix 系统的每个面的简明彻底的解释和实践性的例子。想要全面了解 Linux 的人都不需要再看了 —— 这里没有涉及的内容。

  1. Linux: Rute User’s Tutorial and Exposition 支持印刷版和 html 格式
  2. 是否支持论坛:否

24:高级 Linux 编程

 title=

这本书是写给那些已经熟悉了 C 语言编程的程序员的。这本书采取一种教程式的方式来讲述大多数在 GNU/Linux 系统应用编程中重要的概念和功能特性。如果你是一个已经对 GNU/Linux 系统编程有一定经验的开发者,或者是对其它类 Unix 系统编程有一定经验的开发者,或者对 GNU/Linux 软件开发有兴趣,或者想要从非 Unix 系统环境转换到 Unix 平台并且已经熟悉了优秀软件的开发原则,那你很适合读这本书。另外,你会发现这本书同样适合于 C 和 C++ 编程。

  1. 高级 Linux 编程 支持印刷版和 PDF 格式
  2. 是否支持论坛:否

25: LPI 101 Course Notes

 title=

LPIC 1、2、3 级是用于 Linux 系统管理员认证的。这个网站提供了 LPI 101 和 LPI 102 的测试训练。这些是根据 GNU 自由文档协议 GNU Free Documentation Licence (FDL)发布的。这些课程材料基于 Linux 国际专业协会的 LPI 101 和 102 考试的目标。这个课程是为了提供给你一些必备的 Linux 系统的操作和管理的技能。

  1. LPI 训练手册 支持 PDF 格式
  2. 是否支持论坛:否

26: FLOSS 手册

 title=

FLOSS 手册是一系列关于自由和开源软件以及用于创建它们的工具和使用这些工具的社区的手册。社区的成员包含作者、编辑、设计师、软件开发者、积极分子等。这些手册中说明了怎样安装使用一些自由和开源软件,如何操作(比如设计和维持在线安全)开源软件,这其中也包含如何使用或支持自由软件和格式的自由文化服务手册。你也会发现关于一些像 VLC、 Linux 视频编辑、 Linux、 OLPC / SUGAR、 GRAPHICS 等软件的手册。

  1. 你可以浏览 FOSS 手册 支持 Wiki 格式
  2. 是否支持论坛:否

27:Linux 入门包

 title=

刚接触 Linux 这个美好世界?想找一个简单的入门方式?你可以下载一个 130 页的指南来入门。这个指南会向你展示如何在你的个人电脑上安装 Linux,如何浏览桌面,掌握最主流行的 Linux 程序和修复可能出现的问题的方法。

  1. Linux 入门包支持 PDF 格式
  2. 是否支持论坛:否

28:Linux.com - Linux 信息来源

Linux.com 是 Linux 基金会的一个产品。这个网站上提供一些新闻、指南、教程和一些关于 Linux 的其它信息。利用全球 Linux 用户的力量来通知、写作、连接 Linux 的事务。

  1. 在线访问 Linux.com
  2. 是否支持论坛:是

29: LWN

LWN 是一个注重自由软件及用于 Linux 和其它类 Unix 操作系统的软件的网站。这个网站有周刊、基本上每天发布的单独文章和文章的讨论对话。该网站提供有关 Linux 和 FOSS 相关的开发、法律、商业和安全问题的全面报道。

  1. 在线访问 lwn.net
  2. 是否支持论坛:否

30:Mac OS X 相关网站

与 Mac OS X 相关网站的快速链接:

  • Mac OS X 提示 —— 这个网站专用于苹果的 Mac OS X Unix 操作系统。网站有很多有关 Bash 和 Mac OS X 的使用建议、技巧和教程
  • Mac OS 开发库 —— 苹果拥有大量和 OS X 开发相关的优秀系列内容。不要忘了看一看 bash shell 脚本入门
  • Apple 知识库 - 这个有点像 RHN 的知识库。这个网站提供了所有苹果产品包括 OS X 相关的指南和故障报修建议。

30: NetBSD

(LCTT 译注:没错,又一个 30)

NetBSD 是另一个基于 BSD Unix 操作系统的自由开源操作系统。NetBSD 项目专注于系统的高质量设计、稳定性和性能。由于 NetBSD 的可移植性和伯克利式的许可证,NetBSD 常用于嵌入式系统。这个网站提供了一些 NetBSD 官方文档和各种第三方文档的链接。

  1. 在线访问 netbsd 文档,支持 html、PDF 格式
  2. 是否支持论坛:否

你要做的事

这是我的个人列表,这可能并不完全是权威的,因此如果你有你自己喜欢的独特 Unix/Linux 网站,可以在下方参与评论分享。

// 图片来源: Flickr photo PanelSwitchman。一些连接是用户在我们的 Facebook 粉丝页面上建议添加的。

关于作者

作者是 nixCraft 的创建者和经验丰富的系统管理员以及 Linux 操作系统 / Unix shell 脚本的培训师。它曾与全球客户及各行各业合作,包括 IT、教育,国防和空间研究以及一些非营利部门。可以关注作者的 TwitterFacebookGoogle+


via: https://www.cyberciti.biz/tips/linux-unix-bsd-documentations.html

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

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

sudo 命令允许特权用户以 root 用户身份运行全部或部分命令,但是理解其能做什么和不能做什么很有帮助。

在你想要使用超级权限临时运行一条命令时,sudo 命令非常方便,但是当它不能如你期望的工作时,你也会遇到一些麻烦。比如说你想在某些日志文件结尾添加一些重要的信息,你可能会尝试这样做:

$ echo "Important note" >> /var/log/somelog
-bash: /var/log/somelog: Permission denied

好吧,看起来你似乎需要一些额外的特权。一般来说,你不能使用你的用户账号向系统日志中写入东西。我们使用 sudo 再尝试一次吧。

$ sudo !!
sudo echo "Important note" >> /var/log/somelog
-bash: /var/log/somelog: Permission denied

嗯,它还是没有啥反应。我们来试点不同的吧。

$ sudo 'echo "Important note" >> /var/log/somelog'
sudo: echo "Important note" >> /var/log/somelog: command not found

也可以查看:在 Linux 下排查故障的宝贵提示和技巧

接下来该干什么?

上面在执行完第一条命令后的回应显示,我们缺少向日志文件写入时必须的特权。第二次,我们使用 root 权限运行了第一次的命令,但是返回了一个“没有权限”的错误。第三次,我们把整个命令放在一个引号里面再运行了一遍,返回了一个“没有发现命令”的错误。所以,到底错在哪里了呢?

  • 第一条命令:没有 root 特权,你无法向这个日志中写入东西。
  • 第二条命令:你的超级权限没有延伸到重定向。
  • 第三条命令:sudo 不理解你用引号括起来的整个 “命令”。

而且如果你的用户还未添加到 sudo 用户组的时候,如果尝试使用 sudo,你可能已经看到过像下面的这么一条错误了:

nemo is not in the sudoers file. This incident will be reported.

你可以做什么?

一个相当简单的选择就是使用 sudo 命令暂时成为 root。鉴于你已经有了 sudo 特权,你可以使用下面的命令执行此操作:

$ sudo su
[sudo] password for nemo:
#

注意这个改变的提示符表明了你的新身份。然后你就可以以 root 运行之前的命令了:

# echo "Important note" >> /var/log/somelog

接着你可以输入 ^d 返回你之前的身份。当然了,一些 sudo 的配置可能会阻止你使用 sudo 命令成为 root。

另一个切换用户为 root 的方法是仅用 su 命令,但是这需要你知道 root 密码。许多人被赋予了访问 sudo 的权限,而并不知道 root 密码,所以这并不是总是可行。

(采用 su 直接)切换到 root 之后,你就可以以 root 的身份运行任何你想执行的命令了。这种方式的问题是:1) 每个想要使用 root 特权的人都需要事先知道 root 的密码(这样不很安全);2) 如果在运行需要 root 权限的特定命令后未能退出特权状态,你的系统可能会受到一些重大错误的波及。sudo 命令旨在允许您仅在真正需要时使用 root 权限,并控制每个 sudo 用户应具有的 root 权限。它也可以使你在使用完 root 特权之后轻松地回到普通用户的状态。

另外请注意,整个讨论的前提是你可以正常地访问 sudo,并且你的访问权限没有受限。详细的内容后面会介绍到。

还有一个选择就是使用一个不同的命令。如果通过编辑文件从而在其后添加内容是一种选择的话,你也许可以使用 sudo vi /var/log/somelog,虽然编辑一个活跃的日志文件通常不是一个好主意,因为系统可能会频繁的向这个文件中进行写入操作。

最后一个但是有点复杂的选择是,使用下列命令之一可以解决我们之前看到的问题,但是它们涉及到了很多复杂的语法。第一个命令允许你在得到 “没有权限” 的拒绝之后可以使用 !! 重复你的命令:

$ sudo echo "Important note" >> /var/log/somelog
-bash: /var/log/somelog: Permission denied
$ !!:gs/>/|sudo tee -a /    <=====
$ tail -1 /var/log/somelog
Important note

第二种是通过 sudo 命令,把你想要添加的信息传递给 tee。注意,-a 指定了你要附加文本到目标文件:

$ echo "Important note" | sudo tee -a /var/log/somelog
$ tail -1 /var/log/somelog
Important note

sudo 有多可控?

回答这个问题最快速的回答就是,它取决于管理它的人。大多数 Linux 的默认设置都非常简单。如果一个用户被安排到了一个特别的组中,例如 wheel 或者 admin 组,那这个用户无需知道 root 的密码就可以拥有运行任何命令的能力。这就是大多数 Linux 系统中的默认设置。一旦在 /etc/group 中添加了一个用户到了特权组中,这个用户就可以以 root 的权力运行任何命令。另一方面,可以配置 sudo,以便一些用户只能够以 root 身份运行单一指令或者一组命令中的任何一个。

如果把像下面展示的这些行添加到了 /etc/sudoers 文件中,例如 “nemo” 这个用户可以以 root 身份运行 whoami 命令。在现实中,这可能不会造成任何影响,它非常适合作为一个例子。

# User alias specification
nemo ALL=(root) NOPASSWD: WHOAMI

# Cmnd alias specification
Cmnd_Alias WHOAMI = /usr/bin/whoami

注意,我们添加了一个命令别名(Cmnd_Alias),它指定了一个可以运行的命令的全路径,以及一个用户别名,允许这个用户无需密码就可以使用 sudo 执行的单个命令。

当 nemo 运行 sudo whoami 命令的时候,他将会看到这个:

$ sudo whoami
root

注意这个,因为 nemo 使用 sudo 执行了这条命令,whoami 会显示该命令运行时的用户是 root

至于其他的命令,nemo 将会看到像这样的一些内容:

$ sudo date
[sudo] password for nemo:
Sorry, user nemo is not allowed to execute '/bin/date' as root on butterfly.

sudo 的默认设置

在默认路径中,我们会利用像下面展示的 /etc/sudoers 文件中的几行:

$ sudo egrep "admin|sudo" /etc/sudoers
# Members of the admin group may gain root privileges
%admin ALL=(ALL) ALL        <=====
# Allow members of group sudo to execute any command
%sudo ALL=(ALL:ALL) ALL     <=====

在这几行中,%admin%sudo 都说明了任何添加到这些组中的人都可以使用 sudo 命令以 root 的身份运行任何命令。

下面列出的是 /etc/group 中的一行,它意味着每一个在该组中列出的成员,都拥有了 sudo 特权,而无需在 /etc/sudoers 中进行任何修改。

sudo:x:27:shs,nemo

总结

sudo 命令意味着你可以根据需要轻松地部署超级用户的访问权限,而且只有在需要的时候才能赋予用户非常有限的特权访问权限。你可能会遇到一些与简单的 sudo command 不同的问题,不过在 sudo 的回应中应该会显示你遇到了什么问题。


via: https://www.networkworld.com/article/3322504/linux/selectively-deploying-your-superpowers-on-linux.html

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

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

GPaste 是一个剪贴板管理系统,它包含了库、守护程序以及命令行和 Gnome 界面(使用原生 Gnome Shell 扩展)。

剪贴板管理器能够跟踪你正在复制和粘贴的内容,从而能够访问以前复制的项目。GPaste 带有原生的 Gnome Shell 扩展,是那些寻找 Gnome 剪贴板管理器的人的完美补充。

GPaste Gnome Shell扩展

在 Gnome 中使用 GPaste,你只需单击顶部面板即可得到可配置的、可搜索的剪贴板历史记录。GPaste 不仅会记住你复制的文本,还能记住文件路径和图像(后者需要在设置中启用,因为默认情况下它被禁用)。

不仅如此,GPaste 还可以检测到增长的行,这意味着当检测到新文本是另一个文本的增长时,它会替换它,这对于保持剪贴板整洁非常有用。

在扩展菜单中,你可以暂停 GPaste 跟踪剪贴板,并从剪贴板历史记录或整个历史记录中删除项目。你还会发现一个启动 GPaste 用户界面窗口的按钮。

如果你更喜欢使用键盘,你可以使用快捷键从顶栏开启 GPaste 历史记录(Ctrl + Alt + H)或打开全部的 GPaste GUI(Ctrl + Alt + G)。

该工具还包含这些键盘快捷键(可以更改):

  • 从历史记录中删除活动项目: Ctrl + Alt + V
  • 将活动项目显示为密码(在 GPaste 中混淆剪贴板条目): Ctrl + Alt + S
  • 将剪贴板同步到主选择: Ctrl + Alt + O
  • 将主选择同步到剪贴板:Ctrl + Alt + P
  • 将活动项目上传到 pastebin 服务:Ctrl + Alt + U

GPaste GUI

GPaste 窗口界面提供可供搜索的剪贴板历史记录(包括清除、编辑或上传项目的选项)、暂停 GPaste 跟踪剪贴板的选项、重启 GPaste 守护程序,备份当前剪贴板历史记录,还有它的设置。

GPaste GUI

在 GPaste 界面中,你可以更改以下设置:

  • 启用或禁用 Gnome Shell 扩展
  • 将守护程序状态与扩展程序的状态同步
  • 主选区生效历史
  • 使剪贴板与主选区同步
  • 图像支持
  • 修整条目
  • 检测增长行
  • 保存历史
  • 历史记录设置,如最大历史记录大小、内存使用情况、最大文本长度等
  • 键盘快捷键

下载 GPaste

Gpaste 项目页面没有链接到任何 GPaste 二进制文件,它只有源码安装说明。非 Debian 或 Ubuntu 的 Linux 发行版的用户(你可以在下面找到 GPaste 安装说明)可以在各自的发行版仓库中搜索 GPaste。

不要将 GPaste 与 Gnome Shell 扩展网站上发布的 GPaste Integration 扩展混淆。这是一个使用 GPaste 守护程序的 Gnome Shell 扩展,它不再维护。内置于 GPaste 中的原生 Gnome Shell 扩展仍然维护。

在 Ubuntu(18.04、16.04)或 Debian(Jessie 和更新版本)中安装 GPaste

对于 Debian,GPaste 可用于 Jessie 和更新版本,而对于 Ubuntu,GPaste 在 16.04 及更新版本的仓库中(因此可在 Ubuntu 18.04 Bionic Beaver 中使用)。

你可以使用以下命令在 Debian 或 Ubuntu 中安装 GPaste(守护程序和 Gnome Shell 扩展):

sudo apt install gnome-shell-extensions-gpaste gpaste

安装完成后,按下 Alt + F2 并输入 r 重新启动 Gnome Shell,然后按回车键。现在应该启用了 GPaste Gnome Shell 扩展,其图标应显示在顶部 Gnome Shell 面板上。如果没有,请使用 Gnome Tweaks(Gnome Tweak Tool)启用扩展。

DebianUbuntu 的 GPaste 3.28.0 中有一个错误,如果启用了图像支持选项会导致它崩溃,所以现在不要启用此功能。这在 GPaste 3.28.2 中被标记为已修复,但 Debian 和 Ubuntu 仓库中尚未提供此包。


via: https://www.linuxuprising.com/2018/08/gpaste-is-great-clipboard-manager-for.html

作者:Logix 选题:lujun9972 译者:geekpi 校对:wxy

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

在一个美好的夜晚,你的肚子拒绝消化你在晚餐吃的大块披萨,所以你不得不在睡梦中冲进洗手间。

在浴室里,当你在思考为什么会发生这种情况时,你听到一个来自通风口的低沉声音:“嘿,我是蝙蝠侠。”

这时,你会怎么做呢?

在你恐慌并处于关键时刻之前,蝙蝠侠说:“我需要你的帮助。我是一个超级极客,但我不懂 HTML。我需要用 HTML 写一封情书,你愿意帮助我吗?”

谁会拒绝蝙蝠侠的请求呢,对吧?所以让我们用 HTML 来写一封蝙蝠侠的情书。

你的第一个 HTML 文件

HTML 网页与你电脑上的其它文件一样。就同一个 .doc 文件以 MS Word 打开,.jpg 文件在图像查看器中打开一样,一个 .html 文件在浏览器中打开。

那么,让我们来创建一个 .html 文件。你可以在 Notepad 或其它任何编辑器中完成此任务,但我建议使用 VS Code。在这里下载并安装 VS Code。它是免费的,也是我唯一喜欢的微软产品。

在系统中创建一个目录,将其命名为 “HTML Practice”(不带引号)。在这个目录中,再创建一个名为 “Batman’s Love Letter”(不带引号)的目录,这将是我们的项目根目录。这意味着我们所有与这个项目相关的文件都会在这里。

打开 VS Code,按下 ctrl+n 创建一个新文件,按下 ctrl+s 保存文件。切换到 “Batman’s Love Letter” 文件夹并将其命名为 “loveletter.html”,然后单击保存。

现在,如果你在文件资源管理器中双击它,它将在你的默认浏览器中打开。我建议使用 Firefox 来进行 web 开发,但 Chrome 也可以。

让我们将这个过程与我们已经熟悉的东西联系起来。还记得你第一次拿到电脑吗?我做的第一件事是打开 MS Paint 并绘制一些东西。你在 Paint 中绘制一些东西并将其另存为图像,然后你可以在图像查看器中查看该图像。之后,如果要再次编辑该图像,你在 Paint 中重新打开它,编辑并保存它。

我们目前的流程非常相似。正如我们使用 Paint 创建和编辑图像一样,我们使用 VS Code 来创建和编辑 HTML 文件。就像我们使用图像查看器查看图像一样,我们使用浏览器来查看我们的 HTML 页面。

HTML 中的段落

我们有一个空的 HTML 文件,以下是蝙蝠侠想在他的情书中写的第一段。

“After all the battles we fought together, after all the difficult times we saw together, and after all the good and bad moments we’ve been through, I think it’s time I let you know how I feel about you.”

复制这些到 VS Code 中的 loveletter.html。单击 “View -> Toggle Word Wrap (alt+z)” 自动换行。

保存并在浏览器中打开它。如果它已经打开,单击浏览器中的刷新按钮。

瞧!那是你的第一个网页!

我们的第一段已准备就绪,但这不是在 HTML 中编写段落的推荐方法。我们有一种特定的方法让浏览器知道一个文本是一个段落。

如果你用 <p></p> 来包裹文本,那么浏览器将识别 <p></p> 中的文本是一个段落。我们这样做:

<p>After all the battles we fought together, after all the difficult times we saw together, after all the good and bad moments we've been through, I think it's time I let you know how I feel about you.</p>

通过在 <p></p>中编写段落,你创建了一个 HTML 元素。一个网页就是 HTML 元素的集合。

让我们首先来认识一些术语:<p> 是开始标签,</p> 是结束标签,“p” 是标签名称。元素开始和结束标签之间的文本是元素的内容。

“style” 属性

在上面,你将看到文本覆盖屏幕的整个宽度。

我们不希望这样。没有人想要阅读这么长的行。让我们设定段落宽度为 550px。

我们可以通过使用元素的 style 属性来实现。你可以在其 style 属性中定义元素的样式(例如,在我们的示例中为宽度)。以下行将在 p 元素上创建一个空样式属性:

<p style="">...</p>

你看到那个空的 "" 了吗?这就是我们定义元素外观的地方。现在我们要将宽度设置为 550px。我们这样做:

<p style="width:550px;">
    After all the battles we fought together, after all the difficult times we saw together, after all the good and bad moments we've been through, I think it's time I let you know how I feel about you.
</p>

我们将 width 属性设置为 550px,用冒号 : 分隔,以分号 ; 结束。

另外,注意我们如何将 <p></p> 放在单独的行中,文本内容用一个制表符缩进。像这样设置代码使其更具可读性。

HTML 中的列表

接下来,蝙蝠侠希望列出他所钦佩的人的一些优点,例如:

You complete my darkness with your light. I love:
- the way you see good in the worst things
- the way you handle emotionally difficult situations
- the way you look at Justice
I have learned a lot from you. You have occupied a special place in my heart over time.

这看起来很简单。

让我们继续,在 </p> 下面复制所需的文本:

<p style="width:550px;">
    After all the battles we faught together, after all the difficult times we saw together, after all the good and bad moments we've been through, I think it's time I let you know how I feel about you.
</p>
<p style="width:550px;">
    You complete my darkness with your light. I love:
    - the way you see good in the worse
    - the way you handle emotionally difficult situations
    - the way you look at Justice
    I have learned a lot from you. You have occupied a special place in my heart over the time.
</p>

保存并刷新浏览器。

哇!这里发生了什么,我们的列表在哪里?

如果你仔细观察,你会发现没有显示换行符。在代码中我们在新的一行中编写列表项,但这些项在浏览器中显示在一行中。

如果你想在 HTML(新行)中插入换行符,你必须使用 <br>。让我们来使用 <br>,看看它长什么样:

<p style="width:550px;">
    After all the battles we faught together, after all the difficult times we saw together, after all the good and bad moments we've been through, I think it's time I let you know how I feel about you.
</p>
<p style="width:550px;">
    You complete my darkness with your light. I love: <br>
    - the way you see good in the worse <br>
    - the way you handle emotionally difficult situations <br>
    - the way you look at Justice <br>
    I have learned a lot from you. You have occupied a special place in my heart over the time.
</p>

保存并刷新:

好的,现在它看起来就像我们想要的那样!

另外,注意我们没有写一个 </br>。有些标签不需要结束标签(它们被称为自闭合标签)。

还有一件事:我们没有在两个段落之间使用 <br>,但第二个段落仍然是从一个新行开始,这是因为 <p> 元素会自动插入换行符。

我们使用纯文本编写列表,但是有两个标签可以供我们使用来达到相同的目的:<ul> and <li>

让我们解释一下名字的意思:ul 代表 无序列表 Unordered List ,li 代表 列表项目 List Item 。让我们使用它们来展示我们的列表:

<p style="width:550px;">
    After all the battles we faught together, after all the difficult times we saw together, after all the good and bad moments we've been through, I think it's time I let you know how I feel about you.
</p>
<p style="width:550px;">
  You complete my darkness with your light. I love:
  <ul>
    <li>the way you see good in the worse</li>
    <li>the way you handle emotionally difficult situations</li>
    <li>the way you look at Justice</li>
  </ul>
    I have learned a lot from you. You have occupied a special place in my heart over the time.
</p>

在复制代码之前,注意差异部分:

  • 我们删除了所有的 <br>,因为每个 <li> 会自动显示在新行中
  • 我们将每个列表项包含在 <li></li> 之间
  • 我们将所有列表项的集合包裹在 <ul></ul> 之间
  • 我们没有像 <p> 元素那样定义 <ul> 元素的宽度。这是因为 <ul><p> 的子节点,<p> 已经被约束到 550px,所以 <ul> 不会超出这个范围。

让我们保存文件并刷新浏览器以查看结果:

你会立即注意到在每个列表项之前显示了重点标志。我们现在不需要在每个列表项之前写 “-”。

经过仔细检查,你会注意到最后一行超出 550px 宽度。这是为什么?因为 HTML 不允许 <ul> 元素出现在 <p> 元素中。让我们将第一行和最后一行放在单独的 <p> 元素中:

<p style="width:550px;">
    After all the battles we faught together, after all the difficult times we saw together, after all the good and bad moments we've been through, I think it's time I let you know how I feel about you.
</p>
<p style="width:550px;">
    You complete my darkness with your light. I love:
</p>
<ul style="width:550px;">
  <li>the way you see good in the worse</li>
  <li>the way you handle emotionally difficult situations</li>
  <li>the way you look at Justice</li>
</ul>
<p style="width:550px;">
    I have learned a lot from you. You have occupied a special place in my heart over the time.
</p>

保存并刷新。

注意,这次我们还定义了 <ul> 元素的宽度。那是因为我们现在已经将 <ul> 元素放在了 <p> 元素之外。

定义情书中所有元素的宽度会变得很麻烦。我们有一个特定的元素用于此目的:<div> 元素。一个 <div> 元素就是一个通用容器,用于对内容进行分组,以便轻松设置样式。

让我们用 <div> 元素包装整个情书,并为其赋予宽度:550px 。

<div style="width:550px;">
  <p>
    After all the battles we faught together, after all the difficult times we saw together, after all the good and bad moments we've been through, I think it's time I let you know how I feel about you.
  </p>
  <p>
    You complete my darkness with your light. I love:
  </p>
  <ul>
    <li>the way you see good in the worse</li>
    <li>the way you handle emotionally difficult situations</li>
    <li>the way you look at Justice</li>
  </ul>
  <p>
    I have learned a lot from you. You have occupied a special place in my heart over the time.
  </p>
</div>

棒极了,我们的代码现在看起来简洁多了。

HTML 中的标题

到目前为止,蝙蝠侠对结果很高兴,他希望在情书上标题。他想写一个标题: “Bat Letter”。当然,你已经看到这个名字了,不是吗?:D

你可以使用 <h1><h2><h3><h4><h5><h6> 标签来添加标题,<h1> 是最大的标题和最主要的标题,<h6> 是最小的标题。

让我们在第二段之前使用 <h1> 做主标题和一个副标题:

<div style="width:550px;">
  <h1>Bat Letter</h1>
  <p>
    After all the battles we faught together, after all the difficult times we saw together, after all the good and bad moments we've been through, I think it's time I let you know how I feel about you.
  </p>
  <h2>You are the light of my life</h2>
  <p>
    You complete my darkness with your light. I love:
  </p>
  <ul>
    <li>the way you see good in the worse</li>
    <li>the way you handle emotionally difficult situations</li>
    <li>the way you look at Justice</li>
  </ul>
  <p>
    I have learned a lot from you. You have occupied a special place in my heart over the time.
  </p>
</div>

保存,刷新。

HTML 中的图像

我们的情书尚未完成,但在继续之前,缺少一件大事:蝙蝠侠标志。你见过是蝙蝠侠的东西但没有蝙蝠侠的标志吗?

并没有。

所以,让我们在情书中添加一个蝙蝠侠标志。

在 HTML 中包含图像就像在一个 Word 文件中包含图像一样。在 MS Word 中,你到 “菜单 -> 插入 -> 图像 -> 然后导航到图像位置为止 -> 选择图像 -> 单击插入”。

在 HTML 中,我们使用 <img> 标签让浏览器知道我们需要加载的图像,而不是单击菜单。我们在 src 属性中写入文件的位置和名称。如果图像在项目根目录中,我们可以简单地在 src 属性中写入图像文件的名称。

在我们深入编码之前,从这里下载蝙蝠侠标志。你可能希望裁剪图像中的额外空白区域。复制项目根目录中的图像并将其重命名为 “bat-logo.jpeg”。

<div style="width:550px;">
  <h1>Bat Letter</h1>
  <img src="bat-logo.jpeg">
  <p>
    After all the battles we faught together, after all the difficult times we saw together, after all the good and bad moments we've been through, I think it's time I let you know how I feel about you.
  </p>
<h2>You are the light of my life</h2>
  <p>
    You complete my darkness with your light. I love:
  </p>
  <ul>
    <li>the way you see good in the worse</li>
    <li>the way you handle emotionally difficult situations</li>
    <li>the way you look at Justice</li>
  </ul>
  <p>
    I have learned a lot from you. You have occupied a special place in my heart over the time.
  </p>
</div>

我们在第 3 行包含了 <img> 标签。这个标签也是一个自闭合的标签,所以我们不需要写 </img>。在 src 属性中,我们给出了图像文件的名称。这个名称应与图像名称完全相同,包括扩展名(.jpeg)及其大小写。

保存并刷新,查看结果。

该死的!刚刚发生了什么?

当使用 <img> 标签包含图像时,默认情况下,图像将以其原始分辨率显示。在我们的例子中,图像比 550px 宽得多。让我们使用 style 属性定义它的宽度:

<div style="width:550px;">
  <h1>Bat Letter</h1>
  <img src="bat-logo.jpeg" style="width:100%">
  <p>
    After all the battles we faught together, after all the difficult times we saw together, after all the good and bad moments we've been through, I think it's time I let you know how I feel about you.
  </p>
<h2>You are the light of my life</h2>
  <p>
    You complete my darkness with your light. I love:
  </p>
  <ul>
    <li>the way you see good in the worse</li>
    <li>the way you handle emotionally difficult situations</li>
    <li>the way you look at Justice</li>
  </ul>
  <p>
    I have learned a lot from you. You have occupied a special place in my heart over the time.
  </p>
</div>

你会注意到,这次我们定义宽度使用了 “%” 而不是 “px”。当我们在 “%” 中定义宽度时,它将占据父元素宽度的百分比。因此,100% 的 550px 将为我们提供 550px。

保存并刷新,查看结果。

太棒了!这让蝙蝠侠的脸露出了羞涩的微笑 :)。

HTML 中的粗体和斜体

现在蝙蝠侠想在最后几段中承认他的爱。他有以下文本供你用 HTML 编写:

“I have a confession to make

It feels like my chest does have a heart. You make my heart beat. Your smile brings a smile to my face, your pain brings pain to my heart.

I don’t show my emotions, but I think this man behind the mask is falling for you.”

当阅读到这里时,你会问蝙蝠侠:“等等,这是给谁的?”蝙蝠侠说:

“这是给超人的。”

你说:哦!我还以为是给神奇女侠的呢。

蝙蝠侠说:不,这是给超人的,请在最后写上 “I love you Superman.”。

好的,我们来写:

<div style="width:550px;">
  <h1>Bat Letter</h1>
  <img src="bat-logo.jpeg" style="width:100%">
  <p>
    After all the battles we faught together, after all the difficult times we saw together, after all the good and bad moments we've been through, I think it's time I let you know how I feel about you.
  </p>
<h2>You are the light of my life</h2>
  <p>
    You complete my darkness with your light. I love:
  </p>
  <ul>
    <li>the way you see good in the worse</li>
    <li>the way you handle emotionally difficult situations</li>
    <li>the way you look at Justice</li>
  </ul>
  <p>
    I have learned a lot from you. You have occupied a special place in my heart over the time.
  </p>
  <h2>I have a confession to make</h2>
  <p>
    It feels like my chest does have a heart. You make my heart beat. Your smile brings smile on my face, your pain brings pain to my heart.
  </p>
  <p>
    I don't show my emotions, but I think this man behind the mask is falling for you.
  </p>
  <p>I love you Superman.</p>
  <p>
    Your not-so-secret-lover, <br>
    Batman
  </p>
</div>

这封信差不多完成了,蝙蝠侠另外想再做两次改变。蝙蝠侠希望在最后段落的第一句中的 “does” 一词是斜体,而 “I love you Superman” 这句话是粗体的。

我们使用 <em><strong> 以斜体和粗体显示文本。让我们来更新这些更改:

<div style="width:550px;">
  <h1>Bat Letter</h1>
  <img src="bat-logo.jpeg" style="width:100%">
  <p>
    After all the battles we faught together, after all the difficult times we saw together, after all the good and bad moments we've been through, I think it's time I let you know how I feel about you.
  </p>
  <h2>You are the light of my life</h2>
  <p>
    You complete my darkness with your light. I love:
  </p>
  <ul>
    <li>the way you see good in the worse</li>
    <li>the way you handle emotionally difficult situations</li>
    <li>the way you look at Justice</li>
  </ul>
  <p>
    I have learned a lot from you. You have occupied a special place in my heart over the time.
  </p>
  <h2>I have a confession to make</h2>
  <p>
    It feels like my chest <em>does</em> have a heart. You make my heart beat. Your smile brings smile on my face, your pain brings pain to my heart.
  </p>
  <p>
    I don't show my emotions, but I think this man behind the mask is falling for you.
  </p>
  <p><strong>I love you Superman.</strong></p>
  <p>
    Your not-so-secret-lover, <br>
    Batman
  </p>
</div>

HTML 中的样式

你可以通过三种方式设置样式或定义 HTML 元素的外观:

  • 内联样式:我们使用元素的 style 属性来编写样式。这是我们迄今为止使用的,但这不是一个好的实践。
  • 嵌入式样式:我们在由 <style></style> 包裹的 “style” 元素中编写所有样式。
  • 链接样式表:我们在具有 .css 扩展名的单独文件中编写所有元素的样式。此文件称为样式表。

让我们来看看如何定义 <div> 的内联样式:

<div style="width:550px;">

我们可以在 <style></style> 里面写同样的样式:

div{
  width:550px;
}

在嵌入式样式中,我们编写的样式是与元素分开的。所以我们需要一种方法来关联元素及其样式。第一个单词 “div” 就做了这样的活。它让浏览器知道花括号 {...} 里面的所有样式都属于 “div” 元素。由于这种语法确定要应用样式的元素,因此它称为一个选择器。

我们编写样式的方式保持不变:属性(width)和值(550px)用冒号(:)分隔,以分号(;)结束。

让我们从 <div><img> 元素中删除内联样式,将其写入 <style> 元素:

<style>
  div{
    width:550px;
  }
  img{
    width:100%;
  }
</style>
<div>
  <h1>Bat Letter</h1>
  <img src="bat-logo.jpeg">
  <p>
    After all the battles we faught together, after all the difficult times we saw together, after all the good and bad moments we've been through, I think it's time I let you know how I feel about you.
  </p>
  <h2>You are the light of my life</h2>
  <p>
    You complete my darkness with your light. I love:
  </p>
  <ul>
    <li>the way you see good in the worse</li>
    <li>the way you handle emotionally difficult situations</li>
    <li>the way you look at Justice</li>
  </ul>
  <p>
    I have learned a lot from you. You have occupied a special place in my heart over the time.
  </p>
  <h2>I have a confession to make</h2>
  <p>
    It feels like my chest <em>does</em> have a heart. You make my heart beat. Your smile brings smile on my face, your pain brings pain to my heart.
  </p>
  <p>
    I don't show my emotions, but I think this man behind the mask is falling for you.
  </p>
  <p><strong>I love you Superman.</strong></p>
  <p>
    Your not-so-secret-lover, <br>
    Batman
  </p>
</div>

保存并刷新,结果应保持不变。

但是有一个大问题,如果我们的 HTML 文件中有多个 <div><img> 元素该怎么办?这样我们在 <style> 元素中为 div 和 img 定义的样式就会应用于页面上的每个 div 和 img。

如果你在以后的代码中添加另一个 div,那么该 div 也将变为 550px 宽。我们并不希望这样。

我们想要将我们的样式应用于现在正在使用的特定 div 和 img。为此,我们需要为 div 和 img 元素提供唯一的 id。以下是使用 id 属性为元素赋予 id 的方法:

<div id="letter-container">

以下是如何在嵌入式样式中将此 id 用作选择器:

#letter-container{
  ...
}

注意 # 符号。它表示它是一个 id,{...} 中的样式应该只应用于具有该特定 id 的元素。

让我们来应用它:

<style>
  #letter-container{
    width:550px;
  }
  #header-bat-logo{
    width:100%;
  }
</style>
<div id="letter-container">
  <h1>Bat Letter</h1>
  <img id="header-bat-logo" src="bat-logo.jpeg">
  <p>
    After all the battles we faught together, after all the difficult times we saw together, after all the good and bad moments we've been through, I think it's time I let you know how I feel about you.
  </p>
  <h2>You are the light of my life</h2>
  <p>
    You complete my darkness with your light. I love:
  </p>
  <ul>
    <li>the way you see good in the worse</li>
    <li>the way you handle emotionally difficult situations</li>
    <li>the way you look at Justice</li>
  </ul>
  <p>
    I have learned a lot from you. You have occupied a special place in my heart over the time.
  </p>
  <h2>I have a confession to make</h2>
  <p>
    It feels like my chest <em>does</em> have a heart. You make my heart beat. Your smile brings smile on my face, your pain brings pain to my heart.
  </p>
  <p>
    I don't show my emotions, but I think this man behind the mask is falling for you.
  </p>
  <p><strong>I love you Superman.</strong></p>
  <p>
    Your not-so-secret-lover, <br>
    Batman
  </p>
</div>

HTML 已经准备好了嵌入式样式。

但是,你可以看到,随着我们包含越来越多的样式,<style></style> 将变得很大。这可能很快会混乱我们的主 HTML 文件。

因此,让我们更进一步,通过将 <style> 标签内的内容复制到一个新文件来使用链接样式。

在项目根目录中创建一个新文件,将其另存为 “style.css”:

#letter-container{
  width:550px;
}
#header-bat-logo{
  width:100%;
}

我们不需要在 CSS 文件中写 <style></style>

我们需要使用 HTML 文件中的 <link> 标签来将新创建的 CSS 文件链接到 HTML 文件。以下是我们如何做到这一点:

<link rel="stylesheet" type="text/css" href="style.css">

我们使用 <link> 元素在 HTML 文档中包含外部资源,它主要用于链接样式表。我们使用的三个属性是:

  • rel:关系。链接文件与文档的关系。具有 .css 扩展名的文件称为样式表,因此我们保留 rel=“stylesheet”。
  • type:链接文件的类型;对于一个 CSS 文件来说它是 “text/css”。
  • href:超文本参考。链接文件的位置。

link 元素的结尾没有 </link>。因此,<link> 也是一个自闭合的标签。

<link rel="gf" type="cute" href="girl.next.door">

如果只是得到一个女朋友,那么很容易:D

可惜没有那么简单,让我们继续前进。

这是我们 “loveletter.html” 的内容:

<link rel="stylesheet" type="text/css" href="style.css">
<div id="letter-container">
  <h1>Bat Letter</h1>
  <img id="header-bat-logo" src="bat-logo.jpeg">
  <p>
    After all the battles we faught together, after all the difficult times we saw together, after all the good and bad moments we've been through, I think it's time I let you know how I feel about you.
  </p>
  <h2>You are the light of my life</h2>
  <p>
    You complete my darkness with your light. I love:
  </p>
  <ul>
    <li>the way you see good in the worse</li>
    <li>the way you handle emotionally difficult situations</li>
    <li>the way you look at Justice</li>
  </ul>
  <p>
    I have learned a lot from you. You have occupied a special place in my heart over the time.
  </p>
  <h2>I have a confession to make</h2>
  <p>
    It feels like my chest <em>does</em> have a heart. You make my heart beat. Your smile brings smile on my face, your pain brings pain to my heart.
  </p>
  <p>
    I don't show my emotions, but I think this man behind the mask is falling for you.
  </p>
  <p><strong>I love you Superman.</strong></p>
  <p>
    Your not-so-secret-lover, <br>
    Batman
  </p>
</div>

“style.css” 内容:

#letter-container{
  width:550px;
}
#header-bat-logo{
  width:100%;
}

保存文件并刷新,浏览器中的输出应保持不变。

一些手续

我们的情书已经准备好给蝙蝠侠,但还有一些正式的片段。

与其他任何编程语言一样,HTML 自出生以来(1990 年)经历过许多版本,当前版本是 HTML5。

那么,浏览器如何知道你使用哪个版本的 HTML 来编写页面呢?要告诉浏览器你正在使用 HTML5,你需要在页面顶部包含 <!DOCTYPE html>。对于旧版本的 HTML,这行不同,但你不需要了解它们,因为我们不再使用它们了。

此外,在之前的 HTML 版本中,我们曾经将整个文档封装在 <html></html> 标签内。整个文件分为两个主要部分:头部在 <head></head> 里面,主体在 <body></body> 里面。这在 HTML5 中不是必须的,但由于兼容性原因,我们仍然这样做。让我们用 <Doctype>, <html><head><body> 更新我们的代码:

<!DOCTYPE html>
<html>
<head>
  <link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<div id="letter-container">
  <h1>Bat Letter</h1>
  <img id="header-bat-logo" src="bat-logo.jpeg">
  <p>
    After all the battles we faught together, after all the difficult times we saw together, after all the good and bad moments we've been through, I think it's time I let you know how I feel about you.
  </p>
  <h2>You are the light of my life</h2>
  <p>
    You complete my darkness with your light. I love:
  </p>
  <ul>
    <li>the way you see good in the worse</li>
    <li>the way you handle emotionally difficult situations</li>
    <li>the way you look at Justice</li>
  </ul>
  <p>
    I have learned a lot from you. You have occupied a special place in my heart over the time.
  </p>
  <h2>I have a confession to make</h2>
  <p>
    It feels like my chest <em>does</em> have a heart. You make my heart beat. Your smile brings smile on my face, your pain brings pain to my heart.
  </p>
  <p>
    I don't show my emotions, but I think this man behind the mask is falling for you.
  </p>
  <p><strong>I love you Superman.</strong></p>
  <p>
    Your not-so-secret-lover, <br>
    Batman
  </p>
</div>
</body>
</html>

主要内容在 <body> 里面,元信息在 <head> 里面。所以我们把 <div> 保存在 <body> 里面并加载 <head> 里面的样式表。

保存并刷新,你的 HTML 页面应显示与之前相同的内容。

HTML 的标题

我发誓,这是最后一次改变。

你可能已经注意到选项卡的标题正在显示 HTML 文件的路径:

我们可以使用 <title> 标签来定义 HTML 文件的标题。标题标签也像链接标签一样在 <head> 内部。让我们我们在标题中加上 “Bat Letter”:

<!DOCTYPE html>
<html>
<head>
  <title>Bat Letter</title>
  <link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<div id="letter-container">
  <h1>Bat Letter</h1>
  <img id="header-bat-logo" src="bat-logo.jpeg">
  <p>
    After all the battles we faught together, after all the difficult times we saw together, after all the good and bad moments we've been through, I think it's time I let you know how I feel about you.
  </p>
  <h2>You are the light of my life</h2>
  <p>
    You complete my darkness with your light. I love:
  </p>
  <ul>
    <li>the way you see good in the worse</li>
    <li>the way you handle emotionally difficult situations</li>
    <li>the way you look at Justice</li>
  </ul>
  <p>
    I have learned a lot from you. You have occupied a special place in my heart over the time.
  </p>
  <h2>I have a confession to make</h2>
  <p>
    It feels like my chest <em>does</em> have a heart. You make my heart beat. Your smile brings smile on my face, your pain brings pain to my heart.
  </p>
  <p>
    I don't show my emotions, but I think this man behind the mask is falling for you.
  </p>
  <p><strong>I love you Superman.</strong></p>
  <p>
    Your not-so-secret-lover, <br>
    Batman
  </p>
</div>
</body>
</html>

保存并刷新,你将看到在选项卡上显示的是 “Bat Letter” 而不是文件路径。

蝙蝠侠的情书现在已经完成。

恭喜!你用 HTML 制作了蝙蝠侠的情书。

我们学到了什么

我们学习了以下新概念:

  • 一个 HTML 文档的结构
  • 在 HTML 中如何写元素(<p></p>
  • 如何使用 style 属性在元素内编写样式(这称为内联样式,尽可能避免这种情况)
  • 如何在 <style>...</style> 中编写元素的样式(这称为嵌入式样式)
  • 在 HTML 中如何使用 <link> 在单独的文件中编写样式并链接它(这称为链接样式表)
  • 什么是标签名称,属性,开始标签和结束标签
  • 如何使用 id 属性为一个元素赋予 id
  • CSS 中的标签选择器和 id 选择器

我们学习了以下 HTML 标签:

  • <p>:用于段落
  • <br>:用于换行
  • <ul><li>:显示列表
  • <div>:用于分组我们信件的元素
  • <h1><h2>:用于标题和子标题
  • <img>:用于插入图像
  • <strong><em>:用于粗体和斜体文字样式
  • <style>:用于嵌入式样式
  • <link>:用于包含外部样式表
  • <html>:用于包裹整个 HTML 文档
  • <!DOCTYPE html>:让浏览器知道我们正在使用 HTML5
  • <head>:包裹元信息,如 <link><title>
  • <body>:用于实际显示的 HTML 页面的主体
  • <title>:用于 HTML 页面的标题

我们学习了以下 CSS 属性:

  • width:用于定义元素的宽度
  • CSS 单位:“px” 和 “%”

朋友们,这就是今天的全部了,下一个教程中见。


作者简介:开发者 + 作者 | supersarkar.com | twitter.com/supersarkar


via: https://medium.freecodecamp.org/for-your-first-html-code-lets-help-batman-write-a-love-letter-64c203b9360b

作者:Kunal Sarkar 译者:MjSeven 校对:wxy

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

简介

在本实验中,你将要实现一个基本的内核功能,要求它能够保护运行的用户模式环境(即:进程)。你将去增强这个 JOS 内核,去配置数据结构以便于保持对用户环境的跟踪、创建一个单一用户环境、将程序镜像加载到用户环境中、并将它启动运行。你也要写出一些 JOS 内核的函数,用来处理任何用户环境生成的系统调用,以及处理由用户环境引进的各种异常。

注意: 在本实验中,术语“环境”“进程” 是可互换的 —— 它们都表示同一个抽象概念,那就是允许你去运行的程序。我在介绍中使用术语“环境”而不是使用传统术语“进程”的目的是为了强调一点,那就是 JOS 的环境和 UNIX 的进程提供了不同的接口,并且它们的语义也不相同。

预备知识

使用 Git 去提交你自实验 2 以后的更改(如果有的话),获取课程仓库的最新版本,以及创建一个命名为 lab3 的本地分支,指向到我们的 lab3 分支上 origin/lab3

athena% cd ~/6.828/lab
athena% add git
athena% git commit -am 'changes to lab2 after handin'
Created commit 734fab7: changes to lab2 after handin
 4 files changed, 42 insertions(+), 9 deletions(-)
athena% git pull
Already up-to-date.
athena% git checkout -b lab3 origin/lab3
Branch lab3 set up to track remote branch refs/remotes/origin/lab3.
Switched to a new branch "lab3"
athena% git merge lab2
Merge made by recursive.
 kern/pmap.c |   42 +++++++++++++++++++
 1 files changed, 42 insertions(+), 0 deletions(-)
athena% 

实验 3 包含一些你将探索的新源文件:

inc/    env.h       Public definitions for user-mode environments
        trap.h      Public definitions for trap handling
        syscall.h   Public definitions for system calls from user environments to the kernel
        lib.h       Public definitions for the user-mode support library
kern/   env.h       Kernel-private definitions for user-mode environments
        env.c       Kernel code implementing user-mode environments
        trap.h      Kernel-private trap handling definitions
        trap.c      Trap handling code
        trapentry.S Assembly-language trap handler entry-points
        syscall.h   Kernel-private definitions for system call handling
        syscall.c   System call implementation code
lib/    Makefrag    Makefile fragment to build user-mode library, obj/lib/libjos.a
        entry.S     Assembly-language entry-point for user environments
        libmain.c   User-mode library setup code called from entry.S
        syscall.c   User-mode system call stub functions
        console.c   User-mode implementations of putchar and getchar, providing console I/O
        exit.c      User-mode implementation of exit
        panic.c     User-mode implementation of panic
user/   *           Various test programs to check kernel lab 3 code

另外,一些在实验 2 中的源文件在实验 3 中将被修改。如果想去查看有什么更改,可以运行:

$ git diff lab2

你也可以另外去看一下 实验工具指南,它包含了与本实验有关的调试用户代码方面的信息。

实验要求

本实验分为两部分:Part A 和 Part B。Part A 在本实验完成后一周内提交;你将要提交你的更改和完成的动手实验,在提交之前要确保你的代码通过了 Part A 的所有检查(如果你的代码未通过 Part B 的检查也可以提交)。只需要在第二周提交 Part B 的期限之前代码检查通过即可。

由于在实验 2 中,你需要做实验中描述的所有正则表达式练习,并且至少通过一个挑战(是指整个实验,不是每个部分)。写出详细的问题答案并张贴在实验中,以及一到两个段落的关于你如何解决你选择的挑战问题的详细描述,并将它放在一个名为 answers-lab3.txt 的文件中,并将这个文件放在你的 lab 目标的根目录下。(如果你做了多个问题挑战,你仅需要提交其中一个即可)不要忘记使用 git add answers-lab3.txt 提交这个文件。

行内汇编语言

在本实验中你可能发现使用了 GCC 的行内汇编语言特性,虽然不使用它也可以完成实验。但至少你需要去理解这些行内汇编语言片段,这些汇编语言(asm 语句)片段已经存在于提供给你的源代码中。你可以在课程 参考资料 的页面上找到 GCC 行内汇编语言有关的信息。

Part A:用户环境和异常处理

新文件 inc/env.h 中包含了在 JOS 中关于用户环境的基本定义。现在就去阅读它。内核使用数据结构 Env 去保持对每个用户环境的跟踪。在本实验的开始,你将只创建一个环境,但你需要去设计 JOS 内核支持多环境;实验 4 将带来这个高级特性,允许用户环境去 fork 其它环境。

正如你在 kern/env.c 中所看到的,内核维护了与环境相关的三个全局变量:

struct Env *envs = NULL;            // All environments
struct Env *curenv = NULL;          // The current env
static struct Env *env_free_list;   // Free environment list

一旦 JOS 启动并运行,envs 指针指向到一个数组,即数据结构 Env,它保存了系统中全部的环境。在我们的设计中,JOS 内核将同时支持最大值为 NENV 个的活动的环境,虽然在一般情况下,任何给定时刻运行的环境很少。(NENV 是在 inc/env.h 中用 #define 定义的一个常量)一旦它被分配,对于每个 NENV 可能的环境,envs 数组将包含一个数据结构 Env 的单个实例。

JOS 内核在 env_free_list 上用数据结构 Env 保存了所有不活动的环境。这样的设计使得环境的分配和回收很容易,因为这只不过是添加或删除空闲列表的问题而已。

内核使用符号 curenv 来保持对任意给定时刻的 当前正在运行的环境 进行跟踪。在系统引导期间,在第一个环境运行之前,curenv 被初始化为 NULL

环境状态

数据结构 Env 被定义在文件 inc/env.h 中,内容如下:(在后面的实验中将添加更多的字段):

struct Env {
    struct Trapframe env_tf;   // Saved registers
    struct Env *env_link;      // Next free Env
    envid_t env_id;            // Unique environment identifier
    envid_t env_parent_id;     // env_id of this env's parent
    enum EnvType env_type;     // Indicates special system environments
    unsigned env_status;       // Status of the environment
    uint32_t env_runs;         // Number of times environment has run

    // Address space
    pde_t *env_pgdir;          // Kernel virtual address of page dir
};

以下是数据结构 Env 中的字段简介:

  • env_tf: 这个结构定义在 inc/trap.h 中,它用于在那个环境不运行时保持它保存在寄存器中的值,即:当内核或一个不同的环境在运行时。当从用户模式切换到内核模式时,内核将保存这些东西,以便于那个环境能够在稍后重新运行时回到中断运行的地方。
  • env_link: 这是一个链接,它链接到在 env_free_list 上的下一个 Env 上。env_free_list 指向到列表上第一个空闲的环境。
  • env_id: 内核在数据结构 Env 中保存了一个唯一标识当前环境的值(即:使用数组 envs 中的特定槽位)。在一个用户环境终止之后,内核可能给另外的环境重新分配相同的数据结构 Env —— 但是新的环境将有一个与已终止的旧的环境不同的 env_id,即便是新的环境在数组 envs 中复用了同一个槽位。
  • env_parent_id: 内核使用它来保存创建这个环境的父级环境的 env_id。通过这种方式,环境就可以形成一个“家族树”,这对于做出“哪个环境可以对谁做什么”这样的安全决策非常有用。
  • env_type: 它用于去区分特定的环境。对于大多数环境,它将是 ENV_TYPE_USER 的。在稍后的实验中,针对特定的系统服务环境,我们将引入更多的几种类型。
  • env_status: 这个变量持有以下几个值之一:

    • ENV_FREE: 表示那个 Env 结构是非活动的,并且因此它还在 env_free_list 上。
    • ENV_RUNNABLE: 表示那个 Env 结构所代表的环境正等待被调度到处理器上去运行。
    • ENV_RUNNING: 表示那个 Env 结构所代表的环境当前正在运行中。
    • ENV_NOT_RUNNABLE: 表示那个 Env 结构所代表的是一个当前活动的环境,但不是当前准备去运行的:例如,因为它正在因为一个来自其它环境的进程间通讯(IPC)而处于等待状态。
    • ENV_DYING: 表示那个 Env 结构所表示的是一个僵尸环境。一个僵尸环境将在下一次被内核捕获后被释放。我们在实验 4 之前不会去使用这个标志。
  • env_pgdir: 这个变量持有这个环境的内核虚拟地址的页目录。

就像一个 Unix 进程一样,一个 JOS 环境耦合了“线程”和“地址空间”的概念。线程主要由保存的寄存器来定义(env_tf 字段),而地址空间由页目录和 env_pgdir 所指向的页表所定义。为运行一个环境,内核必须使用保存的寄存器值和相关的地址空间去设置 CPU。

我们的 struct Env 与 xv6 中的 struct proc 类似。它们都在一个 Trapframe 结构中持有环境(即进程)的用户模式寄存器状态。在 JOS 中,单个的环境并不能像 xv6 中的进程那样拥有它们自己的内核栈。在这里,内核中任意时间只能有一个 JOS 环境处于活动中,因此,JOS 仅需要一个单个的内核栈。

为环境分配数组

在实验 2 的 mem_init() 中,你为数组 pages[] 分配了内存,它是内核用于对页面分配与否的状态进行跟踪的一个表。你现在将需要去修改 mem_init(),以便于后面使用它分配一个与结构 Env 类似的数组,这个数组被称为 envs

练习 1、修改在 kern/pmap.c 中的 mem_init(),以用于去分配和映射 envs 数组。这个数组完全由 Env 结构分配的实例 NENV 组成,就像你分配的 pages 数组一样。与 pages 数组一样,由内存支持的数组 envs 也将在 UENVS(它的定义在 inc/memlayout.h 文件中)中映射用户只读的内存,以便于用户进程能够从这个数组中读取。

你应该去运行你的代码,并确保 check_kern_pgdir() 是没有问题的。

创建和运行环境

现在,你将在 kern/env.c 中写一些必需的代码去运行一个用户环境。因为我们并没有做一个文件系统,因此,我们将设置内核去加载一个嵌入到内核中的静态的二进制镜像。JOS 内核以一个 ELF 可运行镜像的方式将这个二进制镜像嵌入到内核中。

在实验 3 中,GNUmakefile 将在 obj/user/ 目录中生成一些二进制镜像。如果你看到 kern/Makefrag,你将注意到一些奇怪的的东西,它们“链接”这些二进制直接进入到内核中运行,就像 .o 文件一样。在链接器命令行上的 -b binary 选项,将因此把它们链接为“原生的”不解析的二进制文件,而不是由编译器产生的普通的 .o 文件。(就链接器而言,这些文件压根就不是 ELF 镜像文件 —— 它们可以是任何东西,比如,一个文本文件或图片!)如果你在内核构建之后查看 obj/kern/kernel.sym ,你将会注意到链接器很奇怪的生成了一些有趣的、命名很费解的符号,比如像 _binary_obj_user_hello_start_binary_obj_user_hello_end、以及 _binary_obj_user_hello_size。链接器通过改编二进制文件的命令来生成这些符号;这种符号为普通内核代码使用一种引入嵌入式二进制文件的方法。

kern/init.ci386_init() 中,你将写一些代码在环境中运行这些二进制镜像中的一种。但是,设置用户环境的关键函数还没有实现;将需要你去完成它们。

练习 2、在文件 env.c 中,写完以下函数的代码:

  • env_init()

初始化 envs 数组中所有的 Env 结构,然后把它们添加到 env_free_list 中。也称为 env_init_percpu,它通过配置硬件,在硬件上为 level 0(内核)权限和 level 3(用户)权限使用单独的段。

  • env_setup_vm()

为一个新环境分配一个页目录,并初始化新环境的地址空间的内核部分。

  • region_alloc()

为一个新环境分配和映射物理内存

  • load_icode()

你将需要去解析一个 ELF 二进制镜像,就像引导加载器那样,然后加载它的内容到一个新环境的用户地址空间中。

  • env_create()

使用 env_alloc 去分配一个环境,并调用 load_icode 去加载一个 ELF 二进制

  • env_run()

在用户模式中开始运行一个给定的环境

在你写这些函数时,你可能会发现新的 cprintf 动词 %e 非常有用 – 它可以输出一个错误代码的相关描述。比如:

    r = -E_NO_MEM;
    panic("env_alloc: %e", r);

中 panic 将输出消息 env\_alloc: out of memory。

下面是用户代码相关的调用图。确保你理解了每一步的用途。

  • start (kern/entry.S)
  • i386_init (kern/init.c)

    • cons_init
    • mem_init
    • env_init
    • trap_init(到目前为止还未完成)
    • env_create
    • env_run

      • env_pop_tf

在完成以上函数后,你应该去编译内核并在 QEMU 下运行它。如果一切正常,你的系统将进入到用户空间并运行二进制的 hello ,直到使用 int 指令生成一个系统调用为止。在那个时刻将存在一个问题,因为 JOS 尚未设置硬件去允许从用户空间到内核空间的各种转换。当 CPU 发现没有系统调用中断的服务程序时,它将生成一个一般保护异常,找到那个异常并去处理它,还将生成一个双重故障异常,同样也找到它并处理它,并且最后会出现所谓的“三重故障异常”。通常情况下,你将随后看到 CPU 复位以及系统重引导。虽然对于传统的应用程序(在 这篇博客文章 中解释了原因)这是重大的问题,但是对于内核开发来说,这是一个痛苦的过程,因此,在打了 6.828 补丁的 QEMU 上,你将可以看到转储的寄存器内容和一个“三重故障”的信息。

我们马上就会去处理这些问题,但是现在,我们可以使用调试器去检查我们是否进入了用户模式。使用 make qemu-gdb 并在 env_pop_tf 处设置一个 GDB 断点,它是你进入用户模式之前到达的最后一个函数。使用 si 单步进入这个函数;处理器将在 iret 指令之后进入用户模式。然后你将会看到在用户环境运行的第一个指令,它将是在 lib/entry.S 中的标签 start 的第一个指令 cmpl。现在,在 hello 中的 sys_cputs()int $0x30 处使用 b *0x...(关于用户空间的地址,请查看 obj/user/hello.asm )设置断点。这个指令 int 是系统调用去显示一个字符到控制台。如果到 int 还没有运行,那么可能在你的地址空间设置或程序加载代码时发生了错误;返回去找到问题并解决后重新运行。

处理中断和异常

到目前为止,在用户空间中的第一个系统调用指令 int $0x30 已正式寿终正寝了:一旦处理器进入用户模式,将无法返回。因此,现在,你需要去实现基本的异常和系统调用服务程序,因为那样才有可能让内核从用户模式代码中恢复对处理器的控制。你所做的第一件事情就是彻底地掌握 x86 的中断和异常机制的使用。

练习 3、如果你对中断和异常机制不熟悉的话,阅读 80386 程序员手册的第 9 章(或 IA-32 开发者手册的第 5 章)。

在这个实验中,对于中断、异常、以其它类似的东西,我们将遵循 Intel 的术语习惯。由于如 异常 exception 陷阱 trap 中断 interrupt 故障 fault 中止 abort 这些术语在不同的架构和操作系统上并没有一个统一的标准,我们经常在特定的架构下(如 x86)并不去考虑它们之间的细微差别。当你在本实验以外的地方看到这些术语时,它们的含义可能有细微的差别。

受保护的控制转移基础

异常和中断都是“受保护的控制转移”,它将导致处理器从用户模式切换到内核模式(CPL=0)而不会让用户模式的代码干扰到内核的其它函数或其它的环境。在 Intel 的术语中,一个中断就是一个“受保护的控制转移”,它是由于处理器以外的外部异步事件所引发的,比如外部设备 I/O 活动通知。而异常正好与之相反,它是由当前正在运行的代码所引发的同步的、受保护的控制转移,比如由于发生了一个除零错误或对无效内存的访问。

为了确保这些受保护的控制转移是真正地受到保护,处理器的中断/异常机制设计是:当中断/异常发生时,当前运行的代码不能随意选择进入内核的位置和方式。而是,处理器在确保内核能够严格控制的条件下才能进入内核。在 x86 上,有两种机制协同来提供这种保护:

  1. 中断描述符表 处理器确保中断和异常仅能够导致内核进入几个特定的、由内核本身定义好的、明确的入口点,而不是去运行中断或异常发生时的代码。

x86 允许最多有 256 个不同的中断或异常入口点去进入内核,每个入口点都使用一个不同的中断向量。一个向量是一个介于 0 和 255 之间的数字。一个中断向量是由中断源确定的:不同的设备、错误条件、以及应用程序去请求内核使用不同的向量生成中断。CPU 使用向量作为进入处理器的中断描述符表(IDT)的索引,它是内核设置的内核私有内存,GDT 也是。从这个表中的适当的条目中,处理器将加载:

* 将值加载到指令指针寄存器(EIP),指向内核代码设计好的,用于处理这种异常的服务程序。
* 将值加载到代码段寄存器(CS),它包含运行权限为 0—1 级别的、要运行的异常服务程序。(在 JOS 中,所有的异常处理程序都运行在内核模式中,运行级别为 0。)
  1. 任务状态描述符表 处理器在中断或异常发生时,需要一个地方去保存旧的处理器状态,比如,处理器在调用异常服务程序之前的 EIPCS 的原始值,这样那个异常服务程序就能够稍后通过还原旧的状态来回到中断发生时的代码位置。但是对于已保存的处理器的旧状态必须被保护起来,不能被无权限的用户模式代码访问;否则代码中的 bug 或恶意用户代码将危及内核。

基于这个原因,当一个 x86 处理器产生一个中断或陷阱时,将导致权限级别的变更,从用户模式转换到内核模式,它也将导致在内核的内存中发生栈切换。有一个被称为 TSS 的任务状态描述符表规定段描述符和这个栈所处的地址。处理器在这个新栈上推送 SSESPEFLAGSCSEIP、以及一个可选的错误代码。然后它从中断描述符上加载 CSEIP 的值,然后设置 ESPSS 去指向新的栈。

虽然 TSS 很大并且默默地为各种用途服务,但是 JOS 仅用它去定义当从用户模式到内核模式的转移发生时,处理器即将切换过去的内核栈。因为在 JOS 中的“内核模式”仅运行在 x86 的运行级别 0 权限上,当进入内核模式时,处理器使用 TSS 上的 ESP0SS0 字段去定义内核栈。JOS 并不去使用 TSS 的任何其它字段。

异常和中断的类型

所有的 x86 处理器上的同步异常都能够产生一个内部使用的、介于 0 到 31 之间的中断向量,因此它映射到 IDT 就是条目 0-31。例如,一个页故障总是通过向量 14 引发一个异常。大于 31 的中断向量仅用于软件中断,它由 int 指令生成,或异步硬件中断,当需要时,它们由外部设备产生。

在这一节中,我们将扩展 JOS 去处理向量为 0-31 之间的、内部产生的 x86 异常。在下一节中,我们将完成 JOS 的 48(0x30)号软件中断向量,JOS 将(随意选择的)使用它作为系统调用中断向量。在实验 4 中,我们将扩展 JOS 去处理外部生成的硬件中断,比如时钟中断。

一个示例

我们把这些片断综合到一起,通过一个示例来巩固一下。我们假设处理器在用户环境下运行代码,遇到一个除零问题。

  1. 处理器去切换到由 TSS 中的 SS0ESP0 定义的栈,在 JOS 中,它们各自保存着值 GD_KDKSTACKTOP
  2. 处理器在内核栈上推入异常参数,起始地址为 KSTACKTOP
+--------------------+ KSTACKTOP             
| 0x00000 | old SS   |     " - 4
|      old ESP       |     " - 8
|     old EFLAGS     |     " - 12
| 0x00000 | old CS   |     " - 16
|      old EIP       |     " - 20 <---- ESP 
+--------------------+
  1. 由于我们要处理一个除零错误,它将在 x86 上产生一个中断向量 0,处理器读取 IDT 的条目 0,然后设置 CS:EIP 去指向由条目描述的处理函数。
  2. 处理服务程序函数将接管控制权并处理异常,例如中止用户环境。

对于某些类型的 x86 异常,除了以上的五个“标准的”寄存器外,处理器还推入另一个包含错误代码的寄存器值到栈中。页故障异常,向量号为 14,就是一个重要的示例。查看 80386 手册去确定哪些异常推入一个错误代码,以及错误代码在那个案例中的意义。当处理器推入一个错误代码后,当从用户模式中进入内核模式,异常处理服务程序开始时的栈看起来应该如下所示:

    +--------------------+ KSTACKTOP             
    | 0x00000 | old SS   |     " - 4
    |      old ESP       |     " - 8
    |     old EFLAGS     |     " - 12
    | 0x00000 | old CS   |     " - 16
    |      old EIP       |     " - 20
    |     error code     |     " - 24 <---- ESP
    +--------------------+             

嵌套的异常和中断

处理器能够处理来自用户和内核模式中的异常和中断。当收到来自用户模式的异常和中断时才会进入内核模式中,而且,在推送它的旧寄存器状态到栈中和通过 IDT 调用相关的异常服务程序之前,x86 处理器会自动切换栈。如果当异常或中断发生时,处理器已经处于内核模式中(CS 寄存器低位两个比特为 0),那么 CPU 只是推入一些值到相同的内核栈中。在这种方式中,内核可以优雅地处理嵌套的异常,嵌套的异常一般由内核本身的代码所引发。在实现保护时,这种功能是非常重要的工具,我们将在稍后的系统调用中看到它。

如果处理器已经处于内核模式中,并且发生了一个嵌套的异常,由于它并不需要切换栈,它也就不需要去保存旧的 SSESP 寄存器。对于不推入错误代码的异常类型,在进入到异常服务程序时,它的内核栈看起来应该如下图:

    +--------------------+ <---- old ESP
    |     old EFLAGS     |     " - 4
    | 0x00000 | old CS   |     " - 8
    |      old EIP       |     " - 12
    +--------------------+             

对于需要推入一个错误代码的异常类型,处理器将在旧的 EIP 之后,立即推入一个错误代码,就和前面一样。

关于处理器的异常嵌套的功能,这里有一个重要的警告。如果处理器正处于内核模式时发生了一个异常,并且不论是什么原因,比如栈空间泄漏,都不会去推送它的旧的状态,那么这时处理器将不能做任何的恢复,它只是简单地重置。毫无疑问,内核应该被设计为禁止发生这种情况。

设置 IDT

到目前为止,你应该有了在 JOS 中为了设置 IDT 和处理异常所需的基本信息。现在,我们去设置 IDT 以处理中断向量 0-31(处理器异常)。我们将在本实验的稍后部分处理系统调用,然后在后面的实验中增加中断 32-47(设备 IRQ)。

在头文件 inc/trap.hkern/trap.h 中包含了中断和异常相关的重要定义,你需要去熟悉使用它们。在文件kern/trap.h 中包含了到内核的、严格的、秘密的定义,可是在 inc/trap.h 中包含的定义也可以被用到用户级程序和库上。

注意:在范围 0-31 中的一些异常是被 Intel 定义为保留。因为在它们的处理器上从未产生过,你如何处理它们都不会有大问题。你想如何做它都是可以的。

你将要实现的完整的控制流如下图所描述:

      IDT               trapentry.S         trap.c
   
+----------------+                        
|   &handler1    |----> handler1:          trap (struct Trapframe *tf)
|                |         // do stuff      {
|                |         call trap          // handle the exception/interrupt
|                |         // ...           }
+----------------+
|   &handler2    |----> handler2:
|                |        // do stuff
|                |        call trap
|                |        // ...
+----------------+
       .
       .
       .
+----------------+
|   &handlerX    |----> handlerX:
|                |         // do stuff
|                |         call trap
|                |         // ...
+----------------+

每个异常或中断都应该在 trapentry.S 中有它自己的处理程序,并且 trap_init() 应该使用这些处理程序的地址去初始化 IDT。每个处理程序都应该在栈上构建一个 struct Trapframe(查看 inc/trap.h),然后使用一个指针调用 trap()(在 trap.c 中)到 Trapframetrap() 接着处理异常/中断或派发给一个特定的处理函数。

练习 4、编辑 trapentry.Strap.c,然后实现上面所描述的功能。在 trapentry.S 中的宏 TRAPHANDLERTRAPHANDLER_NOEC 将会帮你,还有在 inc/trap.h 中的 T\_* defines。你需要在 trapentry.S 中为每个定义在 inc/trap.h 中的陷阱添加一个入口点(使用这些宏),并且你将有 t、o 提供的 _alltraps,这是由宏 TRAPHANDLER指向到它。你也需要去修改 trap_init() 来初始化 idt,以使它指向到每个在 trapentry.S 中定义的入口点;宏 SETGATE 将有助你实现它。

你的 _alltraps 应该:

  1. 推送值以使栈看上去像一个结构 Trapframe
  2. 加载 GD_KD%ds%es
  3. pushl %esp 去传递一个指针到 Trapframe 以作为一个 trap() 的参数
  4. call traptrap 能够返回吗?)

考虑使用 pushal 指令;它非常适合 struct Trapframe 的布局。

使用一些在 user 目录中的测试程序来测试你的陷阱处理代码,这些测试程序在生成任何系统调用之前能引发异常,比如 user/divzero。在这时,你应该能够成功完成 divzerosoftint、以有 badsegment 测试。

.

小挑战!目前,在 trapentry.S 中列出的 TRAPHANDLER 和他们安装在 trap.c 中可能有许多代码非常相似。清除它们。修改 trapentry.S 中的宏去自动为 trap.c 生成一个表。注意,你可以直接使用 .text.data 在汇编器中切换放置其中的代码和数据。

.

问题

在你的 answers-lab3.txt 中回答下列问题:

  1. 为每个异常/中断设置一个独立的服务程序函数的目的是什么?(即:如果所有的异常/中断都传递给同一个服务程序,在我们的当前实现中能否提供这样的特性?)
  2. 你需要做什么事情才能让 user/softint 程序正常运行?评级脚本预计将会产生一个一般保护故障(trap 13),但是 softint 的代码显示为 int $14。为什么它产生的中断向量是 13?如果内核允许 softintint $14 指令去调用内核页故障的服务程序(它的中断向量是 14)会发生什么事情? “`

本实验的 Part A 部分结束了。不要忘了去添加 answers-lab3.txt 文件,提交你的变更,然后在 Part A 作业的提交截止日期之前运行 make handin

Part B:页故障、断点异常、和系统调用

现在,你的内核已经有了最基本的异常处理能力,你将要去继续改进它,来提供依赖异常服务程序的操作系统原语。

处理页故障

页故障异常,中断向量为 14(T_PGFLT),它是一个非常重要的东西,我们将通过本实验和接下来的实验来大量练习它。当处理器产生一个页故障时,处理器将在它的一个特定的控制寄存器(CR2)中保存导致这个故障的线性地址(即:虚拟地址)。在 trap.c 中我们提供了一个专门处理它的函数的一个雏形,它就是 page_fault_handler(),我们将用它来处理页故障异常。

练习 5、修改 trap_dispatch() 将页故障异常派发到 page_fault_handler() 上。你现在应该能够成功测试 faultreadfaultreadkernelfaultwritefaultwritekernel 了。如果它们中的任何一个不能正常工作,找出问题并修复它。记住,你可以使用 make run-xmake run-x-nox 去重引导 JOS 进入到一个特定的用户程序。比如,你可以运行 make run-hello-nox 去运行 hello 用户程序。

下面,你将进一步细化内核的页故障服务程序,因为你要实现系统调用了。

断点异常

断点异常,中断向量为 3(T_BRKPT),它一般用在调试上,它在一个程序代码中插入断点,从而使用特定的 1 字节的 int3 软件中断指令来临时替换相应的程序指令。在 JOS 中,我们将稍微“滥用”一下这个异常,通过将它打造成一个伪系统调用原语,使得任何用户环境都可以用它来调用 JOS 内核监视器。如果我们将 JOS 内核监视认为是原始调试器,那么这种用法是合适的。例如,在 lib/panic.c 中实现的用户模式下的 panic() ,它在显示它的 panic 消息后运行一个 int3 中断。

练习 6、修改 trap_dispatch(),让它在调用内核监视器时产生一个断点异常。你现在应该可以在 breakpoint 上成功完成测试。

.

小挑战!修改 JOS 内核监视器,以便于你能够从当前位置(即:在 int3 之后,断点异常调用了内核监视器) ‘继续’ 异常,并且因此你就可以一次运行一个单步指令。为了实现单步运行,你需要去理解 EFLAGS 寄存器中的某些比特的意义。

可选:如果你富有冒险精神,找一些 x86 反汇编的代码 —— 即通过从 QEMU 中、或从 GNU 二进制工具中分离、或你自己编写 —— 然后扩展 JOS 内核监视器,以使它能够反汇编,显示你的每步的指令。结合实验 1 中的符号表,这将是你写的一个真正的内核调试器。

.

问题

  1. 在断点测试案例中,根据你在 IDT 中如何初始化断点条目的不同情况(即:你的从 trap_initSETGATE 的调用),既有可能产生一个断点异常,也有可能产生一个一般保护故障。为什么?为了能够像上面的案例那样工作,你需要如何去设置它,什么样的不正确设置才会触发一个一般保护故障?
  2. 你认为这些机制的意义是什么?尤其是要考虑 user/softint 测试程序的工作原理。

系统调用

用户进程请求内核为它做事情就是通过系统调用来实现的。当用户进程请求一个系统调用时,处理器首先进入内核模式,处理器和内核配合去保存用户进程的状态,内核为了完成系统调用会运行有关的代码,然后重新回到用户进程。用户进程如何获得内核的关注以及它如何指定它需要的系统调用的具体细节,这在不同的系统上是不同的。

在 JOS 内核中,我们使用 int 指令,它将导致产生一个处理器中断。尤其是,我们使用 int $0x30 作为系统调用中断。我们定义常量 T_SYSCALL 为 48(0x30)。你将需要去设置中断描述符表,以允许用户进程去触发那个中断。注意,那个中断 0x30 并不是由硬件生成的,因此允许用户代码去产生它并不会引起歧义。

应用程序将在寄存器中传递系统调用号和系统调用参数。通过这种方式,内核就不需要去遍历用户环境的栈或指令流。系统调用号将放在 %eax 中,而参数(最多五个)将分别放在 %edx%ecx%ebx%edi、和 %esi 中。内核将在 %eax 中传递返回值。在 lib/syscall.c 中的 syscall() 中已为你编写了使用一个系统调用的汇编代码。你可以通过阅读它来确保你已经理解了它们都做了什么。

练习 7、在内核中为中断向量 T_SYSCALL 添加一个服务程序。你将需要去编辑 kern/trapentry.Skern/trap.ctrap_init()。还需要去修改 trap_dispatch(),以便于通过使用适当的参数来调用 syscall() (定义在 kern/syscall.c)以处理系统调用中断,然后将系统调用的返回值安排在 %eax 中传递给用户进程。最后,你需要去实现 kern/syscall.c 中的 syscall()。如果系统调用号是无效值,确保 syscall() 返回值一定是 -E_INVAL。为确保你理解了系统调用的接口,你应该去阅读和掌握 lib/syscall.c 文件(尤其是行内汇编的动作),对于在 inc/syscall.h 中列出的每个系统调用都需要通过调用相关的内核函数来处理A。

在你的内核中运行 user/hello 程序(make run-hello)。它应该在控制台上输出 hello, world,然后在用户模式中产生一个页故障。如果没有产生页故障,可能意味着你的系统调用服务程序不太正确。现在,你应该有能力成功通过 testbss 测试。

.

小挑战!使用 sysentersysexit 指令而不是使用 int 0x30iret 来实现系统调用。

sysenter/sysexit 指令是由 Intel 设计的,它的运行速度要比 int/iret 指令快。它使用寄存器而不是栈来做到这一点,并且通过假定了分段寄存器是如何使用的。关于这些指令的详细内容可以在 Intel 参考手册 2B 卷中找到。

在 JOS 中添加对这些指令支持的最容易的方法是,在 kern/trapentry.S 中添加一个 sysenter_handler,在它里面保存足够多的关于用户环境返回、设置内核环境、推送参数到 syscall()、以及直接调用 syscall() 的信息。一旦 syscall() 返回,它将设置好运行 sysexit 指令所需的一切东西。你也将需要在 kern/init.c 中添加一些代码,以设置特殊模块寄存器(MSRs)。在 AMD 架构程序员手册第 2 卷的 6.1.2 节中和 Intel 参考手册的 2B 卷的 SYSENTER 上都有关于 MSRs 的很详细的描述。对于如何去写 MSRs,在这里你可以找到一个添加到 inc/x86.h 中的 wrmsr 的实现。

最后,lib/syscall.c 必须要修改,以便于支持用 sysenter 来生成一个系统调用。下面是 sysenter 指令的一种可能的寄存器布局:

eax - syscall number
edx, ecx, ebx, edi - arg1, arg2, arg3, arg4
esi - return pc
ebp - return esp
esp - trashed by sysenter

GCC 的内联汇编器将自动保存你告诉它的直接加载进寄存器的值。不要忘了同时去保存(push)和恢复(pop)你使用的其它寄存器,或告诉内联汇编器你正在使用它们。内联汇编器不支持保存 %ebp,因此你需要自己去增加一些代码来保存和恢复它们,返回地址可以使用一个像 leal after_sysenter_label, %%esi 的指令置入到 %esi 中。

注意,它仅支持 4 个参数,因此你需要保留支持 5 个参数的系统调用的旧方法。而且,因为这个快速路径并不更新当前环境的 trap 帧,因此,在我们添加到后续实验中的一些系统调用上,它并不适合。

在接下来的实验中我们启用了异步中断,你需要再次去评估一下你的代码。尤其是,当返回到用户进程时,你需要去启用中断,而 sysexit 指令并不会为你去做这一动作。

启动用户模式

一个用户程序是从 lib/entry.S 的顶部开始运行的。在一些配置之后,代码调用 lib/libmain.c 中的 libmain()。你应该去修改 libmain() 以初始化全局指针 thisenv,使它指向到这个环境在数组 envs[] 中的 struct Env。(注意那个 lib/entry.S 中已经定义 envs 去指向到在 Part A 中映射的你的设置。)提示:查看 inc/env.h 和使用 sys_getenvid

libmain() 接下来调用 umain,在 hello 程序的案例中,umain 是在 user/hello.c 中。注意,它在输出 ”hello, world” 之后,它尝试去访问 thisenv->env_id。这就是为什么前面会发生故障的原因了。现在,你已经正确地初始化了 thisenv,它应该不会再发生故障了。如果仍然会发生故障,或许是因为你没有映射 UENVS 区域为用户可读取(回到前面 Part A 中 查看 pmap.c);这是我们第一次真实地使用 UENVS 区域)。

练习 8、添加要求的代码到用户库,然后引导你的内核。你应该能够看到 user/hello 程序会输出 hello, world 然后输出 i am environment 00001000user/hello 接下来会通过调用 sys_env_destroy()(查看lib/libmain.clib/exit.c)尝试去“退出”。由于内核目前仅支持一个用户环境,它应该会报告它毁坏了唯一的环境,然后进入到内核监视器中。现在你应该能够成功通过 hello 的测试。

页故障和内存保护

内存保护是一个操作系统中最重要的特性,通过它来保证一个程序中的 bug 不会破坏其它程序或操作系统本身。

操作系统一般是依靠硬件的支持来实现内存保护。操作系统会告诉硬件哪些虚拟地址是有效的,而哪些是无效的。当一个程序尝试去访问一个无效地址或它没有访问权限的地址时,处理器会在导致故障发生的位置停止程序运行,然后捕获内核中关于尝试操作的相关信息。如果故障是可修复的,内核可能修复它并让程序继续运行。如果故障不可修复,那么程序就不能继续,因为它绝对不会跳过那个导致故障的指令。

作为一个可修复故障的示例,假设一个自动扩展的栈。在许多系统上,内核初始化分配一个单栈页,然后如果程序发生的故障是去访问这个栈页下面的页,那么内核会自动分配这些页,并让程序继续运行。通过这种方式,内核只分配程序所需要的内存栈,但是程序可以运行在一个任意大小的栈的假像中。

对于内存保护,系统调用中有一个非常有趣的问题。许多系统调用接口让用户程序传递指针到内核中。这些指针指向用户要读取或写入的缓冲区。然后内核在执行系统调用时废弃这些指针。这样就有两个问题:

  1. 内核中的页故障可能比用户程序中的页故障多的多。如果内核在维护它自己的数据结构时发生页故障,那就是一个内核 bug,而故障服务程序将使整个内核(和整个系统)崩溃。但是当内核废弃了由用户程序传递给它的指针后,它就需要一种方式去记住那些废弃指针所导致的页故障其实是代表用户程序的。
  2. 一般情况下内核拥有比用户程序更多的权限。用户程序可以传递一个指针到系统调用,而指针指向的区域有可能是内核可以读取或写入而用户程序不可访问的区域。内核必须要非常小心,不能被废弃的这种指针欺骗,因为这可能导致泄露私有信息或破坏内核的完整性。

由于以上的原因,内核在处理由用户程序提供的指针时必须格外小心。

现在,你可以通过使用一个简单的机制来仔细检查所有从用户空间传递给内核的指针来解决这个问题。当一个程序给内核传递指针时,内核将检查它的地址是否在地址空间的用户部分,然后页表才允许对内存的操作。

这样,内核在废弃一个用户提供的指针时就绝不会发生页故障。如果内核出现这种页故障,它应该崩溃并终止。

练习 9、如果在内核模式中发生一个页故障,修改 kern/trap.c 去崩溃。

提示:判断一个页故障是发生在用户模式还是内核模式,去检查 tf_cs 的低位比特即可。

阅读 kern/pmap.c 中的 user_mem_assert 并在那个文件中实现 user_mem_check

修改 kern/syscall.c 去常态化检查传递给系统调用的参数。

引导你的内核,运行 user/buggyhello。环境将被毁坏,而内核将不会崩溃。你将会看到:

[00001000] user_mem_check assertion failure for va 00000001
[00001000] free env 00001000
Destroyed the only environment - nothing more to do!

最后,修改在 kern/kdebug.c 中的 debuginfo_eip,在 usdstabs、和 stabstr 上调用 user_mem_check。如果你现在运行 user/breakpoint,你应该能够从内核监视器中运行回溯,然后在内核因页故障崩溃前看到回溯进入到 lib/libmain.c。是什么导致了这个页故障?你不需要去修复它,但是你应该明白它是如何发生的。

注意,刚才实现的这些机制也同样适用于恶意用户程序(比如 user/evilhello)。

练习 10、引导你的内核,运行 user/evilhello。环境应该被毁坏,并且内核不会崩溃。你应该能看到:

[00000000] new env 00001000
...
[00001000] user_mem_check assertion failure for va f010000c
[00001000] free env 00001000

本实验到此结束。确保你通过了所有的等级测试,并且不要忘记去写下问题的答案,在 answers-lab3.txt 中详细描述你的挑战练习的解决方案。提交你的变更并在 lab 目录下输入 make handin 去提交你的工作。

在动手实验之前,使用 git statusgit diff 去检查你的变更,并不要忘记去 git add answers-lab3.txt。当你完成后,使用 git commit -am 'my solutions to lab 3’ 去提交你的变更,然后 make handin 并关注这个指南。


via: https://pdos.csail.mit.edu/6.828/2018/labs/lab3/

作者:csail.mit 选题:lujun9972 译者:qhwdw 校对:wxy

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

如果你想在 Ubuntu 18.04 或任何其他 Linux 发行版上使用 GNOME、MATE 或 Budgie 桌面环境在多个显示器上显示不同的壁纸,这个小工具将帮助你实现这一点。

多显示器设置通常会在 Linux 上出现多个问题,但我不打算在本文中讨论这些问题。我有另外一篇关于 Linux 上多显示器支持的文章。

如果你使用多台显示器,也许你想为每台显示器设置不同的壁纸。我不确定其他 Linux 发行版和桌面环境,但是 GNOME 桌面 的 Ubuntu 本身并不提供此功能。

不要烦恼!在本教程中,我将向你展示如何使用 GNOME 桌面环境为 Linux 发行版上的每个显示器设置不同的壁纸。

在 Ubuntu 18.04 和其他 Linux 发行版上为每个显示器设置不同的壁纸

Different wallaper on each monitor in Ubuntu

我将使用一个名为 HydraPaper 的小工具在不同的显示器上设置不同的背景。HydraPaper 是一个基于 GTK 的应用,用于为 GNOME 桌面环境中的每个显示器设置不同的背景。

它还支持 MATEBudgie 桌面环境。这意味着 Ubuntu MATE 和 Ubuntu Budgie 用户也可以从这个应用中受益。

使用 FlatPak 在 Linux 上安装 HydraPaper

使用 FlatPak 可以轻松安装 HydraPaper。Ubuntu 18.04 已 经提供对 FlatPaks 的支持,所以你需要做的就是下载应用文件并双击在 GNOME 软件中心中打开它。

你可以参考这篇文章来了解如何在你的发行版启用 FlatPak 支持。启用 FlatPak 支持后,只需从 FlatHub 下载并安装即可。

使用 HydraPaper 在不同的显示器上设置不同的背景

安装完成后,只需在应用菜单中查找 HydraPaper 并启动应用。你将在此处看到“图片”文件夹中的图像,因为默认情况下,应用会从用户的“图片”文件夹中获取图像。

你可以添加自己的文件夹来保存壁纸。请注意,它不会递归地查找图像。如果你有嵌套文件夹,它将只显示顶部文件夹中的图像。

Setting up different wallpaper for each monitor on Linux

使用 HydraPaper 很简单。只需为每个显示器选择壁纸,然后单击顶部的应用按钮。你可以轻松地用 HDMI 标识来识别外部显示器。

Setting up different wallpaper for each monitor on Linux

你还可以将选定的壁纸添加到“收藏夹”以便快速访问。这样做会将“最喜欢的壁纸”从“壁纸”选项卡移动到“收藏夹”选项卡。

Setting up different wallpaper for each monitor on Linux

你不需要在每次启动时启动 HydraPaper。为不同的显示器设置不同的壁纸后,设置将被保存,即使重新启动后你也会看到所选择的壁纸。这当然是预期的行为,但我想特别提一下。

HydraPaper 的一大缺点在于它的设计工作方式。你可以看到,HydraPaper 将你选择的壁纸拼接成一张图像并将其拉伸到屏幕上,给人的印象是每个显示器上都有不同的背景。当你移除外部显示器时,这将成为一个问题。

例如,当我尝试使用没有外接显示器的笔记本电脑时,它向我展示了这样的背景图像。

Dual Monitor wallpaper HydraPaper

很明显,这不是我所期望的。

你喜欢它吗?

HydraPaper 使得在不同的显示器上设置不同的背景变得很方便。它支持超过两个显示器和不同的显示器方向。只有所需功能的简单界面使其成为那些总是使用双显示器的人的理想应用。

如何在 Linux 上为不同的显示器设置不同的壁纸?你认为 HydraPaper 是值得安装的应用吗?

请分享您的观点,另外如果你看到这篇文章,请在各种社交媒体渠道上分享,如 Twitter 和 Reddit


via: https://itsfoss.com/wallpaper-multi-monitor/

作者:Abhishek Prakash 选题:lujun9972 译者:geekpi 校对:wxy

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