分类 技术 下的文章

我们都知道,在 Linux 上,包括设备在内的一切都是文件。Linux 管理员每天应该会多次执行文件创建活动(可能是 20 次,50 次,甚至是更多,这依赖于他们的环境)。如果你想 在Linux上创建一个特定大小的文件,查看前面的这个链接。

高效创建一个文件是非常重要的能力。为什么我说高效?如果你了解一些高效进行你当前活动的方式,你就可以事半功倍。这将会节省你很多的时间。你可以把这些有用的时间用到到其他重要的事情上。

我下面将会介绍多个在 Linux 上创建文件的方法。我建议你选择几个简单高效的来辅助你的工作。你不必安装下列的任何一个命令,因为它们已经作为 Linux 核心工具的一部分安装到你的系统上了。

创建文件可以通过以下六个方式来完成。

  • >:标准重定向符允许我们创建一个 0KB 的空文件。
  • touch:如果文件不存在的话,touch 命令将会创建一个 0KB 的空文件。
  • echo:通过一个参数显示文本的某行。
  • printf:用于显示在终端给定的文本。
  • cat:它串联并打印文件到标准输出。
  • vi/vim:Vim 是一个向上兼容 Vi 的文本编辑器。它常用于编辑各种类型的纯文本。
  • nano:是一个简小且用户友好的编辑器。它复制了 pico 的外观和优点,但它是自由软件。
  • head:用于打印一个文件开头的一部分。
  • tail:用于打印一个文件的最后一部分。
  • truncate:用于缩小或者扩展文件的尺寸到指定大小。

在 Linux 上使用重定向符(>)创建一个文件

标准重定向符允许我们创建一个 0KB 的空文件。它通常用于重定向一个命令的输出到一个新文件中。在没有命令的情况下使用重定向符号时,它会创建一个文件。

但是它不允许你在创建文件时向其中输入任何文本。然而它对于不是很勤劳的管理员是非常简单有用的。只需要输入重定向符后面跟着你想要的文件名。

$ > daygeek.txt

使用 ls 命令查看刚刚创建的文件。

$ ls -lh daygeek.txt
-rw-rw-r-- 1 daygeek daygeek 0 Feb 4 02:00 daygeek.txt

在 Linux 上使用 touch 命令创建一个文件

touch 命令常用于将每个文件的访问和修改时间更新为当前时间。

如果指定的文件名不存在,将会创建一个新的文件。touch 不允许我们在创建文件的同时向其中输入一些文本。它默认创建一个 0KB 的空文件。

$ touch daygeek1.txt

使用 ls 命令查看刚刚创建的文件。

$ ls -lh daygeek1.txt
-rw-rw-r-- 1 daygeek daygeek 0 Feb 4 02:02 daygeek1.txt

在 Linux 上使用 echo 命令创建一个文件

echo 内置于大多数的操作系统中。它常用于脚本、批处理文件,以及作为插入文本的单个命令的一部分。

它允许你在创建一个文件时就向其中输入一些文本。当然也允许你在之后向其中输入一些文本。

$ echo "2daygeek.com is a best Linux blog to learn Linux" > daygeek2.txt

使用 ls 命令查看刚刚创建的文件。

$ ls -lh daygeek2.txt
-rw-rw-r-- 1 daygeek daygeek 49 Feb 4 02:04 daygeek2.txt

可以使用 cat 命令查看文件的内容。

$ cat daygeek2.txt
2daygeek.com is a best Linux blog to learn Linux

你可以使用两个重定向符 (>>) 添加其他内容到同一个文件。

$ echo "It's FIVE years old blog" >> daygeek2.txt

你可以使用 cat 命令查看添加的内容。

$ cat daygeek2.txt
2daygeek.com is a best Linux blog to learn Linux
It's FIVE years old blog

在 Linux 上使用 printf 命令创建一个新的文件

printf 命令也可以以类似 echo 的方式执行。

printf 命令常用来显示在终端窗口给出的字符串。printf 可以有格式说明符、转义序列或普通字符。

$ printf "2daygeek.com is a best Linux blog to learn Linux\n" > daygeek3.txt

使用 ls 命令查看刚刚创建的文件。

$ ls -lh daygeek3.txt
-rw-rw-r-- 1 daygeek daygeek 48 Feb 4 02:12 daygeek3.txt

使用 cat 命令查看文件的内容。

$ cat daygeek3.txt
2daygeek.com is a best Linux blog to learn Linux

你可以使用两个重定向符 (>>) 添加其他的内容到同一个文件中去。

$ printf "It's FIVE years old blog\n" >> daygeek3.txt

你可以使用 cat 命令查看这个文件中添加的内容。

$ cat daygeek3.txt
2daygeek.com is a best Linux blog to learn Linux
It's FIVE years old blog

在 Linux 中使用 cat 创建一个文件

cat 表示 串联 concatenate 。在 Linux 经常用于读取一个文件中的数据。

cat 是在类 Unix 系统中最常使用的命令之一。它提供了三个与文本文件相关的功能:显示一个文件的内容、组合多个文件的内容到一个输出以及创建一个新的文件。(LCTT 译注:如果 cat 命令后如果不带任何文件的话,下面的命令在回车后也不会立刻结束,回车后的操作可以按 Ctrl-CCtrl-D 来结束。)

$ cat > daygeek4.txt
2daygeek.com is a best Linux blog to learn Linux
It's FIVE years old blog

使用 ls 命令查看创建的文件。

$ ls -lh daygeek4.txt
-rw-rw-r-- 1 daygeek daygeek 74 Feb 4 02:18 daygeek4.txt

使用 cat 命令查看文件的内容。

$ cat daygeek4.txt
2daygeek.com is a best Linux blog to learn Linux
It's FIVE years old blog

如果你想向同一个文件中添加其他内容,使用两个连接的重定向符(>>)。

$ cat >> daygeek4.txt
This website is maintained by Magesh M, It's licensed under CC BY-NC 4.0.

你可以使用 cat 命令查看添加的内容。

$ cat daygeek4.txt
2daygeek.com is a best Linux blog to learn Linux
It's FIVE years old blog
This website is maintained by Magesh M, It's licensed under CC BY-NC 4.0.

在 Linux 上使用 vi/vim 命令创建一个文件

vim 是一个向上兼容 vi 的文本编辑器。它通常用来编辑所有种类的纯文本。在编辑程序时特别有用。

vim 中有很多功能可以用于编辑单个文件。

$ vi daygeek5.txt

2daygeek.com is a best Linux blog to learn Linux
It's FIVE years old blog

使用 ls 查看刚才创建的文件。

$ ls -lh daygeek5.txt
-rw-rw-r-- 1 daygeek daygeek 75 Feb 4 02:23 daygeek5.txt

使用 cat 命令查看文件的内容。

$ cat daygeek5.txt
2daygeek.com is a best Linux blog to learn Linux
It's FIVE years old blog

在 Linux 上使用 nano 命令创建一个文件

nano 是一个编辑器,它是一个自由版本的 pico 克隆。nano 是一个小且用户友好的编辑器。它复制了 pico 的外观及优点,并且是一个自由软件,它添加了 pico 缺乏的一系列特性,像是打开多个文件、逐行滚动、撤销/重做、语法高亮、行号等等。

$ nano daygeek6.txt

2daygeek.com is a best Linux blog to learn Linux
It's FIVE years old blog
This website is maintained by Magesh M, It's licensed under CC BY-NC 4.0.

使用 ls 命令查看创建的文件。

$ ls -lh daygeek6.txt
-rw-rw-r-- 1 daygeek daygeek 148 Feb 4 02:26 daygeek6.txt

使用 cat 命令来查看一个文件的内容。

$ cat daygeek6.txt
2daygeek.com is a best Linux blog to learn Linux
It's FIVE years old blog
This website is maintained by Magesh M, It's licensed under CC BY-NC 4.0.

在 Linux 上使用 head 命令创建一个文件

head 命令通常用于输出一个文件开头的一部分。它默认会打印一个文件的开头 10 行到标准输出。如果有多个文件,则每个文件前都会有一个标题,用来表示文件名。

$ head -c 0K /dev/zero > daygeek7.txt

使用 ls 命令查看创建的文件。

$ ls -lh daygeek7.txt
-rw-rw-r-- 1 daygeek daygeek 0 Feb 4 02:30 daygeek7.txt

在 Linux 上使用 tail 创建一个文件

tail 命令通常用来输出一个文件最后的一部分。它默认会打印每个文件的最后 10 行到标准输出。如果有多个文件,则每个文件前都会有一个标题,用来表示文件名。

$ tail -c 0K /dev/zero > daygeek8.txt

使用 ls 命令查看创建的文件。

$ ls -lh daygeek8.txt
-rw-rw-r-- 1 daygeek daygeek 0 Feb 4 02:31 daygeek8.txt

在 Linux 上使用 truncate 命令创建一个文件

truncate 命令通常用作将一个文件的尺寸缩小或者扩展为某个指定的尺寸。

$ truncate -s 0K daygeek9.txt

使用 ls 命令检查创建的文件。

$ ls -lh daygeek9.txt
-rw-rw-r-- 1 daygeek daygeek 0 Feb 4 02:37 daygeek9.txt

在这篇文章中,我使用这十个命令分别创建了下面的这十个文件。

$ ls -lh daygeek*
-rw-rw-r-- 1 daygeek daygeek 0 Feb 4 02:02 daygeek1.txt
-rw-rw-r-- 1 daygeek daygeek 74 Feb 4 02:07 daygeek2.txt
-rw-rw-r-- 1 daygeek daygeek 74 Feb 4 02:15 daygeek3.txt
-rw-rw-r-- 1 daygeek daygeek 148 Feb 4 02:20 daygeek4.txt
-rw-rw-r-- 1 daygeek daygeek 75 Feb 4 02:23 daygeek5.txt
-rw-rw-r-- 1 daygeek daygeek 148 Feb 4 02:26 daygeek6.txt
-rw-rw-r-- 1 daygeek daygeek 148 Feb 4 02:32 daygeek7.txt
-rw-rw-r-- 1 daygeek daygeek 148 Feb 4 02:32 daygeek8.txt
-rw-rw-r-- 1 daygeek daygeek 148 Feb 4 02:38 daygeek9.txt
-rw-rw-r-- 1 daygeek daygeek 0 Feb 4 02:00 daygeek.txt

via: https://www.2daygeek.com/linux-command-to-create-a-file/

作者:Vinoth Kumar 选题:lujun9972 译者:dianbanjiu 校对:wxy

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

Remmina 的极简用户界面使得远程访问 Linux / Windows 10 变得轻松。

根据维基百科,远程桌面 是一种“软件或者操作系统特性,它可以让个人电脑上的桌面环境在一个系统(通常是电脑,但是也可以是服务器)上远程运行,但在另一个分开的客户端设备显示”。

换句话说,远程桌面是用来访问在另一台电脑上运行的环境的。比如说 ManageIQ/Integration tests 仓库的拉取请求 (PR) 测试系统开放了一个虚拟网络计算 (VNC) 连接端口,使得我能够远程浏览正被实时测试的拉取请求。远程桌面也被用于帮助客户解决电脑问题:在客户的许可下,你可以远程建立 VNC 或者远程桌面协议(RDP)连接来查看或者交互式地访问该电脑以寻找并解决问题。

运用远程桌面连接软件可以建立这些连接。可供选择的软件有很多,我用 Remmina,因为我喜欢它极简、好用的用户界面 (UI)。它是用 GTK+ 编写的,在 GNU GPL 许可证开源。

在这篇文章里,我会解释如何使用 Remmina 客户端从一台 Linux 电脑上远程连接到 Windows 10 系统 和 Red Hat 企业版 Linux 7 系统。

在 Linux 上安装 Remmina

首先,你需要在你用来远程访问其它电脑的的主机上安装 Remmina。如果你用的是 Fedora,你可以运行如下的命令来安装 Remmina:

sudo dnf install -y remmina

如果你想在一个不同的 Linux 平台上安装 Remmina,跟着 安装教程 走。然后你会发现 Remmina 正和你其它软件出现在一起(在这张图片里选中了 Remmina)。

点击图标运行 Remmina,你应该能看到像这样的屏幕:

Remmina 提供不同种类的连接,其中包括用来连接到 Windows 系统的 RDP 和用来连接到 Linux 系统的 VNC。如你在上图左上角所见的,Remmina 的默认设置是 RDP。

连接到 Windows 10

在你通过 RDP 连接到一台 Windows 10 电脑之前,你必须修改权限以允许分享远程桌面并通过防火墙建立连接。

要许可远程桌面分享,在“文件管理器”界面右击“我的电脑 → 属性 → 远程设置”,接着在跳出的窗口中,勾选“在这台电脑上允许远程连接”,再点击“应用”。

然后,允许远程连接通过你的防火墙。首先在“开始菜单”中查找“防火墙设置”,选择“允许应用通过防火墙”。

在打开的窗口中,在“允许的应用和特性”下找到“远程桌面”。根据你用来访问这个桌面的网络酌情勾选“隐私”和/或“公开”列的选框。点击“确定”。

回到你用来远程访问 Windows 主机的 Linux 电脑,打开 Remmina。输入你的 Windows 主机的 IP 地址,敲击回车键。(我怎么在 LinuxWindws 中确定我的 IP 地址?)看到提示后,输入你的用户名和密码,点击“确定”。

如果你被询问是否接受证书,点击“确定”。

你此时应能看到你的 Windows 10 主机桌面。

连接到 Red Hat 企业版 Linux 7

要在你的 RHEL7 电脑上允许远程访问,在 Linux 桌面上打开“所有设置”。

点击分享图标会打开如下的窗口:

如果“屏幕分享”处于关闭状态,点击一下。一个窗口会弹出,你可以滑动到“打开”的位置。如果你想允许远程控制桌面,将“允许远程控制”调到“打开”。你同样也可以在两种访问选项间选择:一个能够让电脑的主要用户接受或者否绝连接要求,另一个能用密码验证连接。在窗口底部,选择被允许连接的网络界面,最后关闭窗口。

接着,从“应用菜单 → 其它 → 防火墙”打开“防火墙设置”。

勾选 “vnc-server”旁边的选框(如下图所示)关闭窗口。接着直接到你远程电脑上的 Remmina,输入你想连接到的 Linux 桌面的 IP 地址,选择 VNC 作为协议,点击回车键。

如果你之前选择的验证选项是“新连接必须询问访问许可”,RHEL 系统用户会看到这样的一个弹窗:

点击“接受”以成功进行远程连接。

如果你选择用密码验证连接,Remmina 会向你询问密码。

输入密码然后“确认”,你应该能连接到远程电脑。

使用 Remmina

Remmina 提供如上图所示的标签化的 UI,就好像一个浏览器一样。在上图所示的左上角你可以看到两个标签:一个是之前建立的 WIndows 10 连接,另一个新的是 RHEL 连接。

在窗口的左侧,有一个有着“缩放窗口”、“全屏模式”、“偏好”、“截屏”、“断开连接”等选项的工具栏。你可以自己探索看那种适合你。

你也可以通过点击左上角的“+”号创建保存过的连接。根据你的连接情况填好表单点击“保存”。以下是一个 Windows 10 RDP 连接的示例:

下次你打开 Remmina 时连接就在那了。

点击一下它,你不用补充细节就可以建立连接了。

补充说明

当你使用远程桌面软件时,你所有的操作都在远程桌面上消耗资源 —— Remmina(或者其它类似软件)仅仅是一种与远程桌面交互的方式。你也可以通过 SSH 远程访问一台电脑,但那将会让你在那台电脑上局限于仅能使用文字的终端。

你也应当注意到当你允许你的电脑远程连接时,如果一名攻击者用这种方法获得你电脑的访问权同样会给你带来严重损失。因此当你不频繁使用远程桌面时,禁止远程桌面连接以及其在防火墙中相关的服务是很明智的做法。


via: https://opensource.com/article/18/6/linux-remote-desktop

作者:Kedar Vijay Kulkarni 选题:lujun9972 译者:tomjlw 校对:wxy

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

Pony,一种“Rust 遇上 Erlang”的语言,让开发快捷、安全、高效、高并发的程序更简单。

Wallaroo Labs,我是工程副总裁,我们正在构建一个用 Pony 编程语言编写的 高性能分布式流处理器。大多数人没有听说过 Pony,但它一直是 Wallaroo 的最佳选择,它也可能成为你的下一个项目的最佳选择。

“一门编程语言只是另一种工具。与语法无关,与表达性无关,与范式或模型无关,仅与解决难题有关。” —Sylvan Clebsch,Pony 的创建者

我是 Pony 项目的贡献者,但在这里我要谈谈为什么 Pony 对于像 Wallaroo 这样的应用是个好选择,并分享我使用 Pony 的方式。如果你对我们为什么使用 Pony 来编写 Wallaroo 甚感兴趣,我们有一篇关于它的 博文

Pony 是什么?

你可以把 Pony 想象成某种“Rust 遇上 Erlang”的东西。Pony 有着最引人注目的特性,它们是:

  • 类型安全
  • 存储安全
  • 异常安全
  • 无数据竞争
  • 无死锁

此外,它可以被编译为高效的本地代码,它是在开放的情况下开发的,在两句版 BSD 许可证下发布。

以上说的功能不少,但在这里我将重点关注那些对我们公司来说采用 Pony 至关重要的功能。

为什么使用 Pony?

使用大多数我们现有的工具编写快速、安全、高效、高并发的程序并非易事。“快速、高效、高并发”是可实现的目标,但加入“安全”之后,就困难了许多。对于 Wallaroo,我们希望同时实现四个目标,而 Pony 让实现它们更加简单。

高并发

Pony 让并发变得简单。部分是通过提供一个固执的并发方式实现的。在 Pony 语言中,所有的并发都是通过 Actor 模型 进行的。

Actor 模型以在 Erlang 和 Akka 中的实现最为著名。Actor 模型出现于上世纪 70 年代,细节因实现方式而异。不变的是,所有计算都由通过异步消息进行通信的 actor 来执行。

你可以用这种方式来看待 Actor 模型:面向对象中的对象是状态 + 同步方法,而 actor 是状态 + 异步方法。

当一个 actor 收到一个消息时,它执行相应的方法。该方法可以在只有该 actor 可访问的状态下运行。Actor 模型允许我们以并发安全的方式使用可变状态。每个 actor 都是单线程的。一个 actor 中的两个方法绝不会并发运行。这意味着,在给定的 actor 中,数据更新不会引起数据竞争或通常与线程和可变状态相关的其他问题。

快速高效

Pony actor 通过一个高效的工作窃取调度程序来调度。每个可用的 CPU 都有一个单独 Pony 调度程序。这种每个核心一个线程的并发模型是 Pony 尝试与 CPU 协同工作以尽可能高效运行的一部分。Pony 运行时尝试尽可能利用 CPU 缓存。代码越少干扰缓存,运行得越好。Pony 意在帮你的代码与 CPU 缓存友好相处。

Pony 的运行时还会有每个 actor 的堆,因此在垃圾收集期间,没有 “停止一切” 的垃圾收集步骤。这意味着你的程序总是至少能做一点工作。因此 Pony 程序最终具有非常一致的性能和可预测的延迟。

安全

Pony 类型系统引入了一个新概念:引用能力,它使得数据安全成为类型系统的一部分。Pony 语言中每种变量的类型都包含了有关如何在 actor 之间分享数据的信息。Pony 编译器用这些信息来确认,在编译时,你的代码是无数据竞争和无死锁的。

如果这听起来有点像 Rust,那是因为本来就是这样的。Pony 的引用功能和 Rust 的借用检查器都提供数据安全性;它们只是以不同的方式来接近这个目标,并有不同的权衡。

Pony 适合你吗?

决定是否要在一个非业余爱好的项目上使用一门新的编程语言是困难的。与其他方法想比,你必须权衡工具的适当性和不成熟度。那么,Pony 和你搭不搭呢?

如果你有一个困难的并发问题需要解决,那么 Pony 可能是一个好选择。解决并发应用问题是 Pony 之所以存在的理由。如果你能用一个单线程的 Python 脚本就完成所需操作,那你大概不需要它。如果你有一个困难的并发问题,你应该考虑 Pony 及其强大的无数据竞争、并发感知类型系统。

你将获得一个这样的编译器,它将阻止你引入许多与并发相关的错误,并在运行时为你提供出色的性能特征。

开始使用 Pony

如果你准备好开始使用 Pony,你需要先在 Pony 的网站上访问 学习部分。在这里你会找到安装 Pony 编译器的步骤和学习这门语言的资源。

如果你愿意为你正在使用的这个语言做出贡献,我们会在 GitHub 上为你提供一些 初学者友好的问题

同时,我迫不及待地想在 我们的 IRC 频道Pony 邮件列表 上与你交谈。

要了解更多有关 Pony 的消息,请参阅 Sean Allen 2018 年 7 月 16 日至 19 日在俄勒冈州波特兰举行的 第 20 届 OSCON 会议 上的演讲: Pony,我如何学会停止担心并拥抱未经证实的技术


via: https://opensource.com/article/18/5/pony

作者:Sean T Allen 选题:lujun9972 译者:beamrolling 校对:wxy

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

欢迎来到屏幕系列课程。在本系列中,你将学习在树莓派中如何使用汇编代码控制屏幕,从显示随机数据开始,接着学习显示一个固定的图像和显示文本,然后格式化数字为文本。假设你已经完成了 OK 系列课程的学习,所以在本系列中出现的有些知识将不再重复。

第一节的屏幕课程教你一些关于图形的基础理论,然后用这些理论在屏幕或电视上显示一个图案。

1、入门

预期你已经完成了 OK 系列的课程,以及那个系列课程中在 gpio.ssystemTimer.s 文件中调用的函数。如果你没有完成这些,或你喜欢完美的实现,可以去下载 OK05.s 解决方案。在这里也要使用 main.s 文件中从开始到包含 mov sp,#0x8000 的这一行之前的代码。请删除这一行以后的部分。

2、计算机图形

正如你所认识到的,从根本上来说,计算机是非常愚蠢的。它们只能执行有限数量的指令,仅仅能做一些数学,但是它们也能以某种方式来做很多很多的事情。而在这些事情中,我们目前想知道的是,计算机是如何将一个图像显示到屏幕上的。我们如何将这个问题转换成二进制?答案相当简单;我们为每个颜色设计一些编码方法,然后我们为在屏幕上的每个像素保存一个编码。一个像素就是你的屏幕上的一个非常小的点。如果你离屏幕足够近,你或许能够辨别出你的屏幕上的单个像素,能够看到每个图像都是由这些像素组成的。

将颜色表示为数字有几种方法。在这里我们专注于 RGB 方法,但 HSL 也是很常用的另一种方法。

随着计算机时代的进步,人们希望显示越来越复杂的图形,于是发明了图形卡的概念。图形卡是你的计算机上用来在屏幕上专门绘制图像的第二个处理器。它的任务就是将像素值信息转换成显示在屏幕上的亮度级别。在现代计算机中,图形卡已经能够做更多更复杂的事情了,比如绘制三维图形。但是在本系列教程中,我们只专注于图形卡的基本使用;从内存中取得像素然后把它显示到屏幕上。

不管使用哪种方法,现在马上出现的一个问题就是我们使用的颜色编码。这里有几种选择,每个产生不同的输出质量。为了完整起见,我在这里只是简单概述它们。

名字唯一颜色数量描述示例
单色2每个像素使用 1 位去保存,其中 1 表示白色,0 表示黑色。Monochrome image of a bird
灰度256每个像素使用 1 个字节去保存,使用 255 表示白色,0 表示黑色,介于这两个值之间的所有值表示这两个颜色的一个线性组合。Geryscale image of a bird
8 色8每个像素使用 3 位去保存,第一位表示红色通道,第二位表示绿色通道,第三位表示蓝色通道。8 colour image of a bird
低色值256每个像素使用 8 位去保存,前三位表示红色通道的强度,接下来的三位表示绿色通道的强度,最后两位表示蓝色通道的强度。Low colour image of a bird
高色值65,536每个像素使用 16 位去保存,前五位表示红色通道的强度,接下来的六位表示绿色通道的强度,最后的五位表示蓝色通道的强度。High colour image of a bird
真彩色16,777,216每个像素使用 24 位去保存,前八位表示红色通道,第二个八位表示绿色通道,最后八位表示蓝色通道。True colour image of a bird
RGBA3216,777,216 带 256 级透明度每个像素使用 32 位去保存,前八位表示红色通道,第二个八位表示绿色通道,第三个八位表示蓝色通道。只有一个图像绘制在另一个图像的上方时才考虑使用透明通道,值为 0 时表示下面图像的颜色,值为 255 时表示上面这个图像的颜色,介于这两个值之间的所有值表示这两个图像颜色的混合。
不过这里的一些图像只用了很少的颜色,因为它们使用了一个叫空间抖动的技术。这允许它们以很少的颜色仍然能表示出非常好的图像。许多早期的操作系统就使用了这种技术。

在本教程中,我们将从使用高色值开始。这样你就可以看到图像的构成,它的形成过程清楚,图像质量好,又不像真彩色那样占用太多的空间。也就是说,显示一个比较小的 800x600 像素的图像,它只需要小于 1 MiB 的空间。它另外的好处是它的大小是 2 次幂的倍数,相比真彩色这将极大地降低了获取信息的复杂度。

树莓派和它的图形处理器有一种特殊而奇怪的关系。在树莓派上,首先运行的事实上是图形处理器,它负责启动主处理器。这是很不常见的。最终它不会有太大的差别,但在许多交互中,它经常给人感觉主处理器是次要的,而图形处理器才是主要的。在树莓派上这两者之间依靠一个叫 “邮箱” 的东西来通讯。它们中的每一个都可以为对方投放邮件,这个邮件将在未来的某个时刻被对方收集并处理。我们将使用这个邮箱去向图形处理器请求一个地址。这个地址将是一个我们在屏幕上写入像素颜色信息的位置,我们称为帧缓冲,图形卡将定期检查这个位置,然后更新屏幕上相应的像素。

保存 帧缓冲 frame buffer 给计算机带来了很大的内存负担。基于这种原因,早期计算机经常作弊,比如,保存一屏幕文本,在每次单独刷新时,它只绘制刷新了的字母。

3、编写邮差程序

接下来我们做的第一件事情就是编写一个“邮差”程序。它有两个方法:MailboxRead,从寄存器 r0 中的邮箱通道读取一个消息。而 MailboxWrite,将寄存器 r0 中的头 28 位的值写到寄存器 r1 中的邮箱通道。树莓派有 7 个与图形处理器进行通讯的邮箱通道。但仅第一个对我们有用,因为它用于协调帧缓冲。

消息传递是组件间通讯时使用的常见方法。一些操作系统在程序之间使用虚拟消息进行通讯。

下列的表和示意图描述了邮箱的操作。

表 3.1 邮箱地址

地址大小 / 字节名字描述读 / 写
2000B8804Read接收邮件R
2000B8904Poll不检索接收R
2000B8944Sender发送者信息R
2000B8984Status信息R
2000B89C4Configuration设置RW
2000B8A04Write发送邮件W

为了给指定的邮箱发送一个消息:

  1. 发送者等待,直到 Status 字段的头一位为 0。
  2. 发送者写入到 Write,低 4 位是要发送到的邮箱,高 28 位是要写入的消息。

为了读取一个消息:

  1. 接收者等待,直到 Status 字段的第 30 位为 0。
  2. 接收者读取消息。
  3. 接收者确认消息来自正确的邮箱,否则再次重试。

如果你觉得有信心,你现在已经有足够的信息去写出我们所需的两个方法。如果没有信心,请继续往下看。

与以前一样,我建议你实现的第一个方法是获取邮箱区域的地址。

.globl GetMailboxBase
GetMailboxBase:
ldr r0,=0x2000B880
mov pc,lr

发送程序相对简单一些,因此我们将首先去实现它。随着你的方法越来越复杂,你需要提前去规划它们。规划它们的一个好的方式是写出一个简单步骤列表,详细地列出你需要做的事情,像下面一样。

  1. 我们的输入将要写什么(r0),以及写到什么邮箱(r1)。我们必须验证邮箱的真实性,以及它的低 4 位的值是否为 0。不要忘了验证输入。
  2. 使用 GetMailboxBase 去检索地址。
  3. 读取 Status 字段。
  4. 检查头一位是否为 0。如果不是,回到第 3 步。
  5. 将写入的值和邮箱通道组合到一起。
  6. 写入到 Write

我们来按顺序写出它们中的每一步。

1、这将实现我们验证 r0r1 的目的。tst 是通过计算两个操作数的逻辑与来比较两个操作数的函数,然后将结果与 0 进行比较。在本案例中,它将检查在寄存器 r0 中的输入的低 4 位是否为全 0。

.globl MailboxWrite
MailboxWrite:
tst r0,#0b1111
movne pc,lr
cmp r1,#15
movhi pc,lr
tst reg,#val 计算寄存器 reg#val 的逻辑与,然后将计算结果与 0 进行比较。

2、这段代码确保我们不会覆盖我们的值,或链接寄存器,然后调用 GetMailboxBase

channel .req r1
value .req r2
mov value,r0
push {lr}
bl GetMailboxBase
mailbox .req r0

3、这段代码加载当前状态。

wait1$:
status .req r3
ldr status,[mailbox,#0x18]

4、这段代码检查状态字段的头一位是否为 0,如果不为 0,循环回到第 3 步。

tst status,#0x80000000
.unreq status
bne wait1$

5、这段代码将通道和值组合到一起。

add value,channel
.unreq channel

6、这段代码保存结果到写入字段。

str value,[mailbox,#0x20]
.unreq value
.unreq mailbox
pop {pc}

MailboxRead 的代码和它非常类似。

  1. 我们的输入将从哪个邮箱读取(r0)。我们必须要验证邮箱的真实性。不要忘了验证输入。
  2. 使用 GetMailboxBase 去检索地址。
  3. 读取 Status 字段。
  4. 检查第 30 位是否为 0。如果不为 0,返回到第 3 步。
  5. 读取 Read 字段。
  6. 检查邮箱是否是我们所要的,如果不是返回到第 3 步。
  7. 返回结果。

我们来按顺序写出它们中的每一步。

1、这一段代码来验证 r0 中的值。

.globl MailboxRead
MailboxRead:
cmp r0,#15
movhi pc,lr

2、这段代码确保我们不会覆盖掉我们的值,或链接寄存器,然后调用 GetMailboxBase

channel .req r1
mov channel,r0
push {lr}
bl GetMailboxBase
mailbox .req r0

3、这段代码加载当前状态。

rightmail$:
wait2$:
status .req r2
ldr status,[mailbox,#0x18]

4、这段代码检查状态字段第 30 位是否为 0,如果不为 0,返回到第 3 步。

tst status,#0x40000000
.unreq status
bne wait2$

5、这段代码从邮箱中读取下一条消息。

mail .req r2
ldr mail,[mailbox,#0]

6、这段代码检查我们正在读取的邮箱通道是否为提供给我们的通道。如果不是,返回到第 3 步。

inchan .req r3
and inchan,mail,#0b1111
teq inchan,channel
.unreq inchan
bne rightmail$
.unreq mailbox
.unreq channel

7、这段代码将答案(邮件的前 28 位)移动到寄存器 r0 中。

and r0,mail,#0xfffffff0
.unreq mail
pop {pc}

4、我心爱的图形处理器

通过我们新的邮差程序,我们现在已经能够向图形卡上发送消息了。我们应该发送些什么呢?这对我来说可能是个很难找到答案的问题,因为它不是任何线上手册能够找到答案的问题。尽管如此,通过查找有关树莓派的 GNU/Linux,我们能够找出我们需要发送的内容。

消息很简单。我们描述我们想要的帧缓冲区,而图形卡要么接受我们的请求,给我们返回一个 0,然后用我们写的一个小的调查问卷来填充屏幕;要么发送一个非 0 值,我们知道那表示很遗憾(出错了)。不幸的是,我并不知道它返回的其它数字是什么,也不知道它意味着什么,但我们知道仅当它返回一个 0,才表示一切顺利。幸运的是,对于合理的输入,它总是返回一个 0,因此我们不用过于担心。

由于在树莓派的内存是在图形处理器和主处理器之间共享的,我们能够只发送可以找到我们信息的位置即可。这就是 DMA,许多复杂的设备使用这种技术去加速访问时间。

为简单起见,我们将提前设计好我们的请求,并将它保存到 framebuffer.s 文件的 .data 节中,它的代码如下:

.section .data
.align 4
.globl FrameBufferInfo
FrameBufferInfo:
.int 1024 /* #0 物理宽度 */
.int 768 /* #4 物理高度 */
.int 1024 /* #8 虚拟宽度 */
.int 768 /* #12 虚拟高度 */
.int 0 /* #16 GPU - 间距 */
.int 16 /* #20 位深 */
.int 0 /* #24 X */
.int 0 /* #28 Y */
.int 0 /* #32 GPU - 指针 */
.int 0 /* #36 GPU - 大小 */

这就是我们发送到图形处理器的消息格式。第一对两个关键字描述了物理宽度和高度。第二对关键字描述了虚拟宽度和高度。帧缓冲的宽度和高度就是虚拟的宽度和高度,而 GPU 按需要伸缩帧缓冲去填充物理屏幕。如果 GPU 接受我们的请求,接下来的关键字将是 GPU 去填充的参数。它们是帧缓冲每行的字节数,在本案例中它是 2 × 1024 = 2048。下一个关键字是每个像素分配的位数。使用了一个 16 作为值意味着图形处理器使用了我们上面所描述的高色值模式。值为 24 是真彩色,而值为 32 则是 RGBA32。接下来的两个关键字是 x 和 y 偏移量,它表示当将帧缓冲复制到屏幕时,从屏幕左上角跳过的像素数目。最后两个关键字是由图形处理器填写的,第一个表示指向帧缓冲的实际指针,第二个是用字节数表示的帧缓冲大小。

在这里我非常谨慎地使用了一个 .align 4 指令。正如前面所讨论的,这样确保了下一行地址的低 4 位是 0。所以,我们可以确保将被放到那个地址上的帧缓冲(FrameBufferInfo)是可以发送到图形处理器上的,因为我们的邮箱仅发送低 4 位全为 0 的值。

当设备使用 DMA 时,对齐约束变得非常重要。GPU 预期该消息都是 16 字节对齐的。

到目前为止,我们已经有了待发送的消息,我们可以写代码去发送它了。通讯将按如下的步骤进行:

  1. 写入 FrameBufferInfo + 0x40000000 的地址到邮箱 1。
  2. 从邮箱 1 上读取结果。如果它是非 0 值,意味着我们没有请求一个正确的帧缓冲。
  3. 复制我们的图像到指针,这时图像将出现在屏幕上!

我在步骤 1 中说了一些以前没有提到的事情。我们在发送之前,在帧缓冲地址上加了 0x40000000。这其实是一个给 GPU 的特殊信号,它告诉 GPU 应该如何写到结构上。如果我们只是发送地址,GPU 将写到它的回复上,这样不能保证我们可以通过刷新缓存看到它。缓存是处理器使用的值在它们被发送到存储之前保存在内存中的片段。通过加上 0x40000000,我们告诉 GPU 不要将写入到它的缓存中,这样将确保我们能够看到变化。

因为在那里发生很多事情,因此最好将它实现为一个函数,而不是将它以代码的方式写入到 main.s 中。我们将要写一个函数 InitialiseFrameBuffer,由它来完成所有协调和返回指向到上面提到的帧缓冲数据的指针。为方便起见,我们还将帧缓冲的宽度、高度、位深作为这个方法的输入,这样就很容易地修改 main.s 而不必知道协调的细节了。

再一次,来写下我们要做的详细步骤。如果你有信心,可以略过这一步直接尝试去写函数。

  1. 验证我们的输入。
  2. 写输入到帧缓冲。
  3. 发送 frame buffer + 0x40000000 的地址到邮箱。
  4. 从邮箱中接收回复。
  5. 如果回复是非 0 值,方法失败。我们应该返回 0 去表示失败。
  6. 返回指向帧缓冲信息的指针。

现在,我们开始写更多的方法。以下是上面其中一个实现。

1、这段代码检查宽度和高度是小于或等于 4096,位深小于或等于 32。这里再次使用了条件运行的技巧。相信自己这是可行的。

.section .text
.globl InitialiseFrameBuffer
InitialiseFrameBuffer:
width .req r0
height .req r1
bitDepth .req r2
cmp width,#4096
cmpls height,#4096
cmpls bitDepth,#32
result .req r0
movhi result,#0
movhi pc,lr

2、这段代码写入到我们上面定义的帧缓冲结构中。我也趁机将链接寄存器推入到栈上。

fbInfoAddr .req r3
push {lr}
ldr fbInfoAddr,=FrameBufferInfo
str width,[fbInfoAddr,#0]
str height,[fbInfoAddr,#4]
str width,[fbInfoAddr,#8]
str height,[fbInfoAddr,#12]
str bitDepth,[fbInfoAddr,#20]
.unreq width
.unreq height
.unreq bitDepth

3、MailboxWrite 方法的输入是写入到寄存器 r0 中的值,并将通道写入到寄存器 r1 中。

mov r0,fbInfoAddr
add r0,#0x40000000
mov r1,#1
bl MailboxWrite

4、MailboxRead 方法的输入是写入到寄存器 r0 中的通道,而输出是值读数。

mov r0,#1
bl MailboxRead

5、这段代码检查 MailboxRead 方法的结果是否为 0,如果不为 0,则返回 0。

teq result,#0
movne result,#0
popne {pc}

6、这是代码结束,并返回帧缓冲信息地址。

mov result,fbInfoAddr
pop {pc}
.unreq result
.unreq fbInfoAddr

5、在一帧中一行之内的一个像素

到目前为止,我们已经创建了与图形处理器通讯的方法。现在它已经能够给我们返回一个指向到帧缓冲的指针去绘制图形了。我们现在来绘制一个图形。

第一示例中,我们将在屏幕上绘制连续的颜色。它看起来并不漂亮,但至少能说明它在工作。我们如何才能在帧缓冲中设置每个像素为一个连续的数字,并且要持续不断地这样做。

将下列代码复制到 main.s 文件中,并放置在 mov sp,#0x8000 行之后。

mov r0,#1024
mov r1,#768
mov r2,#16
bl InitialiseFrameBuffer

这段代码使用了我们的 InitialiseFrameBuffer 方法,简单地创建了一个宽 1024、高 768、位深为 16 的帧缓冲区。在这里,如果你愿意可以尝试使用不同的值,只要整个代码中都一样就可以。如果图形处理器没有给我们创建好一个帧缓冲区,这个方法将返回 0,我们最好检查一下返回值,如果出现返回值为 0 的情况,我们打开 OK LED 灯。

teq r0,#0
bne noError$

mov r0,#16
mov r1,#1
bl SetGpioFunction
mov r0,#16
mov r1,#0
bl SetGpio

error$:
b error$

noError$:
fbInfoAddr .req r4
mov fbInfoAddr,r0

现在,我们已经有了帧缓冲信息的地址,我们需要取得帧缓冲信息的指针,并开始绘制屏幕。我们使用两个循环来做实现,一个走行,一个走列。事实上,树莓派中的大多数应用程序中,图片都是以从左到右然后从上到下的顺序来保存的,因此我们也按这个顺序来写循环。

render$:

    fbAddr .req r3
    ldr fbAddr,[fbInfoAddr,#32]
    
    colour .req r0
    y .req r1
    mov y,#768
    drawRow$:
    
        x .req r2
        mov x,#1024
        drawPixel$:
        
            strh colour,[fbAddr]
            add fbAddr,#2
            sub x,#1
            teq x,#0
            bne drawPixel$
        
        sub y,#1
        add colour,#1
        teq y,#0
        bne drawRow$
    
    b render$

.unreq fbAddr
.unreq fbInfoAddr
strh reg,[dest] 将寄存器中的低位半个字保存到给定的 dest 地址上。

这是一个很长的代码块,它嵌套了三层循环。为了帮你理清头绪,我们将循环进行缩进处理,这就有点类似于高级编程语言,而汇编器会忽略掉这些用于缩进的 tab 字符。我们看到,在这里它从帧缓冲信息结构中加载了帧缓冲的地址,然后基于每行来循环,接着是每行上的每个像素。在每个像素上,我们使用一个 strh(保存半个字)命令去保存当前颜色,然后增加地址继续写入。每行绘制完成后,我们增加绘制的颜色号。在整个屏幕绘制完成后,我们跳转到开始位置。

6、看到曙光

现在,你已经准备好在树莓派上测试这些代码了。你应该会看到一个渐变图案。注意:在第一个消息被发送到邮箱之前,树莓派在它的四个角上一直显示一个渐变图案。如果它不能正常工作,请查看我们的排错页面。

如果一切正常,恭喜你!你现在可以控制屏幕了!你可以随意修改这些代码去绘制你想到的任意图案。你还可以做更精彩的渐变图案,可以直接计算每个像素值,因为每个像素包含了一个 Y 坐标和 X 坐标。在下一个 课程 7:Screen 02 中,我们将学习一个更常用的绘制任务:行。


via: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/screen01.html

作者:Alex Chadwick 选题:lujun9972 译者:qhwdw 校对:wxy

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

关于 DNS 和根证书你需要了解的内容。

由于最近发生的一些事件,我们(Privacy Today 组织)感到有必要写一篇关于此事的短文。它适用于所有读者,因此它将保持简单 —— 技术细节可能会在稍后的文章发布。

什么是 DNS,为什么它与你有关?

DNS 的意思是 域名系统 Domain Name System ,你每天都会接触到它。每当你的 Web 浏览器或任何其他应用程序连接到互联网时,它就很可能会使用域名。简单来说,域名就是你键入的地址:例如 duckduckgo.com。你的计算机需要知道它所导向的地方,会向 DNS 解析器寻求帮助。而它将返回类似 176.34.155.23 这样的 IP —— 这就是连接时所需要知道的公开网络地址。 此过程称为 DNS 查找。

这对你的隐私、安全以及你的自由都有一定的影响:

隐私

由于你要求解析器获取域名的 IP,因此它会确切地知道你正在访问哪些站点,并且由于“物联网”(通常缩写为 IoT),甚至它还知道你在家中使用的是哪个设备。

安全

你可以相信解析器返回的 IP 是正确的。有一些检查措施可以确保如此,在正常情况下这一般不是问题。但这些可能措施会被破坏,这就是写作本文的原因。如果返回的 IP 不正确,你可能会被欺骗引向了恶意的第三方 —— 甚至你都不会注意到任何差异。在这种情况下,你的隐私会受到更大的危害,因为不仅会被跟踪你访问了什么网站,甚至你访问的内容也会被跟踪。第三方可以准确地看到你正在查看的内容,收集你输入的个人信息(例如密码)等等。你的整个身份可以轻松接管。

自由

审查通常是通过 DNS 实施的。这不是最有效的方法,但它非常普遍。即使在西方国家,它也经常被公司和政府使用。他们使用与潜在攻击者相同的方法;当你查询 IP 地址时,他们不会返回正确的 IP。他们可以表现得就好像某个域名不存在,或完全将访问指向别处。

DNS 查询的方式

由你的 ISP 提供的第三方 DNS 解析器

大多数人都在使用由其互联网接入提供商(ISP)提供的第三方解析器。当你连接调制解调器时(LCTT 译注:或宽带路由器),这些 DNS 解析器就会被自动取出,而你可能从来没注意过它。

你自己选择的第三方 DNS 解析器

如果你已经知道 DNS 意味着什么,那么你可能会决定使用你选择的另一个 DNS 解析器。这可能会改善这种情况,因为它使你的 ISP 更难以跟踪你,并且你可以避免某些形式的审查。尽管追踪和审查仍然是可能的,但这种方法并没有被广泛使用。

你自己(本地)的 DNS 解析器

你可以自己动手,避免使用别人的 DNS 解析器的一些危险。如果你对此感兴趣,请告诉我们。

根证书

什么是根证书?

每当你访问以 https 开头的网站时,你都会使用它发送的证书与之通信。它使你的浏览器能够加密通信并确保没有人可以窥探。这就是为什么每个人都被告知在登录网站时要注意 https(而不是 http)。证书本身仅用于验证是否为某个域所生成。以及:

这就是根证书的来源。可以其视为一个更高的级别,用来确保其下的级别是正确的。它验证发送给你的证书是否已由证书颁发机构授权。此权限确保创建证书的人实际上是真正的运营者。

这也被称为信任链。默认情况下,你的操作系统包含一组这些根证书,以确保该信任链的存在。

滥用

我们现在知道:

  • DNS 解析器在你发送域名时向你发送 IP 地址
  • 证书允许加密你的通信,并验证它们是否为你访问的域生成
  • 根证书验证该证书是否合法,并且是由真实站点运营者创建的

怎么会被滥用呢?

  • 如前所述,恶意 DNS 解析器可能会向你发送错误的 IP 以进行审查。它们还可以将你导向完全不同的网站。
  • 这个网站可以向你发送假的证书。
  • 恶意的根证书可以“验证”此假证书。

对你来说,这个网站看起来绝对没问题;它在网址中有 https,如果你点击它,它会说已经通过验证。就像你了解到的一样,对吗?不对!

它现在可以接收你要发送给原站点的所有通信。这会绕过想要避免被滥用而创建的检查。你不会收到错误消息,你的浏览器也不会发觉。

而你所有的数据都会受到损害!

结论

风险

  • 使用恶意 DNS 解析器总是会损害你的隐私,但只要你注意 https,你的安全性就不会受到损害。
  • 使用恶意 DNS 解析程序和恶意根证书,你的隐私和安全性将完全受到损害。

可以采取的动作

不要安装第三方根证书!只有非常少的例外情况才需要这样做,并且它们都不适用于一般最终用户。

不要被那些“广告拦截”、“军事级安全”或类似的东西营销噱头所吸引。有一些方法可以自行使用 DNS 解析器来增强你的隐私,但安装第三方根证书永远不会有意义。你正在将自己置身于陷阱之中。

实际看看

警告

有位友好的系统管理员提供了一个现场演示,你可以实时看到自己。这是真事。

千万不要输入私人数据!之后务必删除证书和该 DNS!

如果你不知道如何操作,那就不要安装它。虽然我们相信我们的朋友,但你不要随便安装随机和未知的第三方根证书。

实际演示

链接在这里:http://https-interception.info.tm/

  • 设置所提供的 DNS 解析器
  • 安装所提供的根证书
  • 访问 https://paypal.com 并输入随机登录数据
  • 你的数据将显示在该网站上

延伸信息

如果你对更多技术细节感兴趣,请告诉我们。如果有足够多感兴趣的人,我们可能会写一篇文章,但是目前最重要的部分是分享基础知识,这样你就可以做出明智的决定,而不会因为营销和欺诈而陷入陷阱。请随时提出对你很关注的其他主题。

这篇文章来自 Privacy Today 频道Privacy Today 是一个关于隐私、开源、自由哲学等所有事物的组织!

所有内容均根据 CC BY-NC-SA 4.0 获得许可。(署名 - 非商业性使用 - 共享 4.0 国际)。


via: https://lushka.al/dns-and-certificates/

作者:Anxhelo Lushka 选题:lujun9972 译者:wxy 校对:wxy

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

在你的游戏开发过程中有了 PyGame Zero,和枯燥的模板说再见吧。

Python 是一个很好的入门级编程语言。并且,游戏是一个很好的入门项目:它们是可视化的,自驱动的,并且可以很愉快的与朋友和家人分享。虽然,绝大多数的 Python 写就的库,比如 PyGame ,会让初学者因为忘记微小的细节很容易导致什么都没渲染而感到困扰。

在理解所有部分的作用之前,他们会将其中的许多部分都视为“无意识的模板文件”——需要复制和粘贴到程序中才能使其工作的神奇段落。

PyGame Zero 试图通过在 PyGame 上放置一个抽象层来弥合这一差距,因此它字面上并不需要模板。

我们在说的“字面”,就是在指字面。

这是一个合格的 PyGame Zero 文件:

# This comment is here for clarity reasons

我们可以将它放在一个 game.py 文件里,并运行:

$ pgzrun game.py

这将会展示一个窗口,并运行一个可以通过关闭窗口或按下 CTRL-C 中断的游戏循环。

遗憾的是,这将是一场无聊的游戏。什么都没发生。

为了让它更有趣一点,我们可以画一个不同的背景:

def draw():
    screen.fill((255, 0, 0))

这将会把背景色从黑色换为红色。但是这仍是一个很无聊的游戏,什么都没发生。我们可以让它变的更有意思一点:

colors = [0, 0, 0]

def draw():
    screen.fill(tuple(colors))

def update():
    colors[0] = (colors[0] + 1) % 256

这将会让窗口从黑色开始,逐渐变亮,直到变为亮红色,再返回黑色,一遍一遍循环。

update 函数更新了参数的值,而 draw 基于这些参数渲染这个游戏。

即使是这样,这里也没有任何方式给玩家与这个游戏的交互的方式。让我们试试其他一些事情:

colors = [0, 0, 0]

def draw():
    screen.fill(tuple(colors))

def update():
    colors[0] = (colors[0] + 1) % 256

def on_key_down(key, mod, unicode):
    colors[1] = (colors[1] + 1) % 256

现在,按下按键来提升亮度。

这些包括游戏循环的三个重要部分:响应用户输入,更新参数和重新渲染屏幕。

PyGame Zero 提供了更多功能,包括绘制精灵图和播放声音片段的功能。

试一试,看看你能想出什么类型的游戏!


via: https://opensource.com/article/19/1/pygame-zero

作者:Moshe Zadka 选题:lujun9972 译者:bestony 校对:wxy

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