2020年11月

云原生时代的华为,不但打造了迅猛发展的云服务业务,也为自己的云服务打造了新“引擎”。

云原生时代的容器引擎的变化

随着“云原生”逐渐从一个流行词变成了一个不那么新鲜的技术基座。以 Kubernetes 为代表的容器编排技术、以 Docker、Containerd 占据主要份额的容器引擎,云原生技术也在不断的迭代升级中日益发展成熟。

Sysdig 2019 年的容器使用报告统计,全球整体容器市场规模以高达 30% 的速度增长,容器的规模、密度愈加扩大。在企业内部的容器规模方面,9% 的企业用户容器规模已经达到 5000 以上;在容器密度方面,与 2018 年相比,每台主机中的容器密度提高了 100%,从 15 个增加到了 30 个,其中最大节点密度已经达到 250 个。

而在这一切的背后,容器技术在某些场景中也呈现了一些不足,比如:

  • 在资源敏感环境,或需要部署高密度容器节点时,容器对基础设施的资源占用会急剧升高;
  • 当大规模应用拉起或遇到突发流量时,并发速度可能成为了瓶颈。

因此,主流的 Docker 等容器引擎在特定用例下,看起来有一些力不从心,因此一些针对某种用例进行过专门优化的容器引擎技术这些年纷纷入场。比如说,以 Kata Container 为代表的专门针对容器隔离性不够严格而设计的安全容器技术;以 Container Linux 为代表的专门针对重型应用而设计的系统容器;以 iSula 为代表的专门针对资源受限的边缘计算和 IoT 环境设计的轻量级容器技术。

这里,让我们来看一个源自于摄像头场景中的轻量级容器引擎。

来自摄像头的容器引擎

说起来你可能不信,一个摄像头里面居然还能有容器引擎。

起初,华为为了在智能摄像头上达到快速、简单切换算法应用部署的功能,经过技术研究,他们决定使用容器来实现所需的功能。

一开始,技术团队考虑对开源容器引擎 Docker 进行轻量化改造,对其裁剪和精简化、去除不需要的功能、优化组件结构等,甚至还对 Go 语言环境的编译进行了优化。但是,由于要运行在端侧的嵌入式设备上,这种裁剪和压榨资源的做法所能取得的效果有限。

在这种情况下,针对端侧和 IoT 环境,华为的 iSula 容器团队做了一个大胆的决定,使用 C/C++ 来量身打造一套轻量级的容器引擎!这真是一个大胆而充满勇气的决定。要知道,随着容器技术时代被 Docker 的出现而引爆,开发 Docker 所使用的 Go 语言就成为容器技术领域的首选,几乎所有的容器技术的组件和框架,都是采用 Go 语言开发的。而要使用 C/C++ 语言全新开发一个容器引擎,面临着所有基础组件,甚至一些开发语言缺乏的特性都需要另行解决。比如说,在 C 语言中要解析容器技术中普遍使用 JSON 数据,而 C 语言并没有 Go 语言等现代编程语言内置的反射机制,这就需要自行实现一个合理的 JSON 数据解析引擎。

2017 年,iSula 容器团队开始了重新开发一个容器引擎的计划。

旁白:iSula 是中南美洲亚马逊丛林中的一种非常强大的蚂蚁,被称为“子弹蚁”,因为被它咬一口,犹如被子弹打到那般疼痛,它是世界上最强大的昆虫之一。

所幸的是,虽然拦路虎众多,但是这些付出是有丰厚回报的,采用 C/C++ 开发的容器引擎,也因此具有了 Docker 所不具有的一些优势。相比 Golang 编写的 Docker,使用 C/C++ 实现的 iSula 容器引擎,具有轻、灵、巧、快等特点,不受硬件规格和架构的限制,底噪开销更小,可应用领域更为广泛。在严苛的资源要求环境下,轻量模式下的 iSulad 本身占用资源极低(< 15M),再结合上特殊的轻量化镜像,可以达成极致的资源占用效果。

2018 年,iSula 开始在华为内部的部分产品上使用。2019 年,华为决定将 iSula 开源出来,让开源社区和 iSula 共同发展,因此针对 CRI 接口进行了一次大范围的重构和补全后,与 openEuler 操作系统一并开源发布。

新造的轮子野心大

以 2019 年的统计数据看,容器引擎领域中,Docker 占据了 80% 左右的份额,但是随着 Docker 引擎自身的发展不明朗,以及容器引擎规范的标准化,出现了更多的容器引擎竞争者。这其中,iSula 作为一个悄悄发展了 3 年的轻量级容器引擎,已经迭代到了 2.0 版本,并凭借其“轻快易灵”的优势,逐渐显露出了更大的“野心”。

在智能摄像头资源的端侧大显身手之后,iSula 容器团队决定将它更进一步。得益于 iSula 所打下的良好基础,iSula 团队认为这个引擎具备更大的潜力,可以发展为通用的端、边、云平台一体的容器引擎,提供统一的架构设计来满足云、IoT、边缘计算等多个场景的应用。

虽然由于发展时间较短,加之其起源于端侧场景,目前 iSula 还没有大规模地应用在云计算集群方面,但是从与 iSula 团队沟通了解到,他们对下一步将其推广至更广泛的云计算集群领域充满信心。按照他们的说法,鉴于华为优质的软件开发质量品控,以及社区对 iSula 特有优势的青睐,它的发展值得期许。

当然,事物总是具有两面性,iSula 在取得“轻快易灵”的独特优势的同时,其使用 C/C++ 作为开发语言,也对 iSula 的快速发展带来了一些影响。因为我们知道,合格甚至优秀的 C/C++ 程序员是有多么的难得,这也造成了 iSula 项目开源后,社区贡献者数量和参与的贡献难以取得大的突破。

鉴于此,据 iSula 团队内部消息,他们正在计划将 iSula 逐渐迁移到 Rust 语言来实现,目前已经有部分模块采用 Rust 开发。Rust 作为近些年来一个明星级的系统编程语言,已经在系统编程语言方面显露出来取代 C/C++ 的潜力。如果能够顺利地平滑过渡到 Rust 语言,想必对 iSula 的开发进展、软件质量和社区参与程度,有着积极的作用。

何以轻快易灵?

iSula 是全量的容器软件栈,包括了引擎、网络、存储、工具集与容器操作系统;而 iSulad 作为其中轻量化的容器引擎,可以为多种场景提供灵活、稳定、安全的底层支撑。

根据 iSulad 的设计目标和实现情况,它具有轻、快、易、灵等优势。

iSulad 之轻

iSulad 的第一个使用场景是在端侧设备上,这自然要求这个容器引擎具有轻量级资源占用的特性。再结合为端侧设备特殊定制的轻量化镜像,它可以达成极致的资源占用的效果。

除了在端侧环境,在通用场景下,iSulad 也具有不错的轻量化表现。利用轻量化的 LXC 运行时以及极其轻量的 monitor 进程,这简化了整个调用链路。

iSulad 之快

顺理成章的,采用 C/C++ 语言实现的 iSulad,自然具备运行速度快、底噪低等特性。再加上 iSulad 独特的架构设计,除了启动容器部分需要通过 fork/exec 的方式,其他部分均使用调用函数库的方式加快执行速度。

iSulad 之易

在对 CRI 接口进行了大范围的重构和补全后,iSulad 已经能在相当程度上兼容标准化的容器规范和工具,让使用者的使用习惯和应用迁移变得轻松。

为了使开发者迁移方便,iSulad 在开发一系列迁移工具,以帮助开发者将自己的应用平滑迁移到 iSulad 上来。甚至据透露,iSulad 还会支持热迁移,能更便捷的迁移开发者的应用。

iSulad 之灵

iSulad 还针对不同的使用场景提供了不同的模式,可以根据需要灵活配置切换注重性能的性能模式和注重资源占用的轻模式。

另外,作为一个具有支持全场景容器环境的引擎,iSulad 也支持了多种不同的容器形态,它内置了支持系统容器、安全容器和普通容器以及轻量化容器的支持。

iSula 和 openEuler

iSula 是华为的 openEuler 开源社区旗下的项目之一,因此这个项目也是根植于 openEuler 系统的。这对于推动 openEuler 在企业级应用的发展具有积极意义。

不过,作为一个野心勃勃的容器引擎来说,必然不会将自己局限在某个特定操作系统之上。根据 iSula 团队的信息,目前 iSula 在 openEuler 系统上具有一些独特的优势,但是该团队也在做将 iSula 向其它 Linux 系统迁移的工作,这涉及到内核的一些特殊特性和补丁,需要得到 Linux 主线内核的支持和与内核开发者社区的沟通。

推动云原生的新引擎

毋庸置疑,容器计算已经成为云计算领域的主流。无论你是否愿意,考虑将企业的传统计算环境和古典虚拟机环境迁移到以容器计算为代表的现代云计算平台,已经是大部分 CTO 和架构师们需要迫切考虑的工作了。

而华为开源的 iSula 容器引擎,相比 Docker,是一种新的容器解决方案,它提供了统一的架构设计来满足 CT 和 IT 领域的不同需求。这匹崭露头角的新黑马,是华为攻略云原生领域的新引擎之一。

无需去历数华为在云原生领域做了多少事情,这个崭露头角的 iSula 容器引擎只是华为云这辆快车上的一枚新引擎,它将会同其它开源组件将华为云带到什么高度,让我们拭目以待。

如何清除 APT 缓存?你只需使用这个 apt-get 命令选项:

sudo apt-get clean

但是,清理 APT 缓存不仅仅是运行上面的命令。

在本教程中,我将解释什么是 APT 缓存、为什么会使用它、为什么你要清理它,以及关于清理 APT 缓存你应该知道的其他事情。

我将在这里使用 Ubuntu 作为参考,但由于这是关于 APT 的,因此它也适用于 Debian 和其他基于 Debian 和 Ubuntu 的发行版,比如 Linux Mint、Deepin 等等。

什么是 APT 缓存?为什么要使用它?

当你使用 apt-getapt 命令安装一个软件包时(或在软件中心安装 DEB 包),APT 包管理器会以 .deb 格式下载软件包及其依赖关系,并将其保存在 /var/cache/apt/archives 文件夹中。

下载时,apt 将 deb 包保存在 /var/cache/apt/archives/partial 目录下。当 deb 包完全下载完毕后,它会被移到 /var/cache/apt/archives 目录下。

下载完包的 deb 文件及其依赖关系后,你的系统就会从这些 deb 文件中安装包

现在你明白缓存的用途了吧?系统在安装软件包之前,需要一个地方把软件包文件存放在某个地方。如果你了解 Linux 目录结构,你就会明白,/var/cache 是合适的地方。

为什么安装包后要保留缓存?

下载的 deb 文件在安装完成后并不会立即从目录中删除。如果你删除了一个软件包,然后重新安装,你的系统会在缓存中查找这个软件包,并从这里获取它,而不是重新下载(只要缓存中的软件包版本与远程仓库中的版本相同)。

这样就快多了。你可以自己尝试一下,看看一个程序第一次安装,删除后再安装需要多长时间。你可以使用 time 命令来了解完成一个命令需要多长时间time sudo apt install package_name

我找不到任何关于缓存保留策略的内容,所以我无法说明 Ubuntu 会在缓存中保留下载的包多长时间。

你应该清理 APT 缓存吗?

这取决于你。如果你的根目录下的磁盘空间用完了,你可以清理 APT 缓存来回收磁盘空间。这是 Ubuntu 上释放磁盘空间的几种方法之一。

使用 du 命令检查缓存占用了多少空间:

有的时候,这可能会占用几百兆,如果你正在运行一个服务器,这些空间可能是至关重要的。

如何清理 APT 缓存?

如果你想清除 APT 缓存,有一个专门的命令来做。所以不要去手动删除缓存目录。只要使用这个命令就可以了:

sudo apt-get clean

这将删除 /var/cache/apt/archives 目录的内容(除了锁文件)。以下是 apt-get clean 命令模拟删除内容:

还有一个命令是关于清理 APT 缓存的:

sudo apt-get autoclean

clean 不同的是,autoclean 只删除那些无法从仓库中下载的包。

假设你安装了包 xyz。它的 deb 文件仍然保留在缓存中。如果现在仓库中有新的 xyz 包,那么缓存中现有的这个 xyz 包就已经过时了,没有用了。autoclean 选项会删除这种不能再下载的无用包。

删除 apt 缓存安全吗?

是的,清除 APT 创建的缓存是完全安全的。它不会对系统的性能产生负面影响。也许如果你重新安装软件包,下载时间会更长一些,但也仅此而已。

再说一次,使用 apt-get clean 命令。它比手动删除缓存目录更快、更简单。

你也可以使用像 StacerBleachbit 这样的图形工具来实现这个目的。

总结

在写这篇文章的时候,新的 apt 命令没有这样的内置选项。不过,为了保持向后的兼容性,仍然可以运行 apt clean (内部应该是运行了 apt-get clean)。请参考这篇文章来了解 apt 和 apt-get 的区别

我希望你觉得这个关于 APT 缓存的解释很有趣。虽然这不是什么必要的东西,但了解这些小东西会让你对你的 Linux 系统更加了解。

欢迎你在评论区提出反馈和建议。


via: https://itsfoss.com/clear-apt-cache/

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

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

使用 apt-cache 命令,你可以在本地 APT 缓存中搜索软件包的详细信息。在本教程中学习使用 apt-cache 命令。

apt-cache 命令是用来干什么的?

APT 包管理器工作在软件包元数据的本地缓存上。元数据通常由包名、版本、描述、依赖关系、仓库和开发者等信息组成。通过 apt-cache 命令,你可以查询这个本地 APT 缓存并获得相关信息。

你可以搜索一个包的可用性、它的版本号、它的依赖关系等等。我将通过实例告诉你如何使用 apt-cache命令。

APT 缓存的位置是 /var/lib/apt/lists/ 目录。缓存哪些仓库元数据取决于你的源列表中 /etc/apt/sources.list 文件中添加的仓库,以及位于 /etc/apt/sources.list.d 目录下的额外仓库文件。

令人惊讶的是,apt-cache 并不能清除 APT 缓存。为此,你必须使用 apt-get clean 命令

不用说,APT 打包系统是在 Debian 和基于 Debian 的 Linux 发行版上使用的,比如 Ubuntu、Linux Mint、Elementary OS 等。你不能在 Arch 或 Fedora 上使用它。

使用 apt-cache 命令

就像其他 Linux 命令一样,apt-cache 也有一些可用的选项,你可以随时参考它的手册页来了解这些选项。

然而,你可能并不需要使用所有的选项。这就是为什么我在本教程中只向你展示 apt-cache 命令中最常见和最有用的例子。

始终更新

更新本地 APT 缓存以与远程仓库同步是一个好主意。如何做到这一点呢?你可以使用命令:

sudo apt update

搜索软件包

apt-cache 命令最常见的用途是查找软件包。你可以使用一个正则表达式来搜索本地 APT 缓存中的包。

apt-cache search package_name

默认情况下,它会在软件包的名称和描述中查找搜索关键词。它按字母顺序显示匹配的软件包及其简短的描述。

你也可以缩小搜索范围,只在软件包名称中查找搜索词。

apt-cache search --names-only package_name

如果你想知道所有匹配软件包的完整细节,你可以使用 --full 标志。

获取详细的包装信息

如果你知道确切的软件包名称(或者你已经成功地通过搜索找到了它),你可以得到软件包的详细元数据信息。

apt-cache show package_name

你可以看到软件包元数据中的所有细节,比如名称、版本、开发者、维护者、仓库、长短描述、软件包大小甚至是校验和。

还有一个选项 showpkg 可以显示软件包的名称、版本、正向和反向依赖关系等信息。

apt-cache showpkg package_name

apt-cache 的策略

这是 apt-cache 命令中很少使用的一个选项。policy 选项可以帮助你调试与 preference 文件相关的问题。

如果你指定了软件包的名称,它将显示该软件包是否已经安装,在哪个版本的仓库中可用,以及它的优先级。

默认情况下,每个已安装的软件包版本的优先级为 100,未安装的软件包的优先级为 500。同一软件包可能有多个不同优先级的版本。APT 会安装优先级较高的版本,除非安装的版本较新。

如果不理解这个部分,也没关系。对于一个普通的 Linux 用户来说,会极少纠结于这么深的软件包管理知识。

检查软件包的依赖关系和反向依赖关系。

你可以在安装之前(甚至在安装之后)检查一个包的依赖关系。它还会显示所有可能满足依赖关系的软件包。

apt-cache depends package

你也可以通过 apt-cahce 检查反向依赖关系来检查哪些包是依赖于某个包的。

坦白说,看到 Ansible 这样的 DevOps 工具对 Cowsay 这样有趣的 Linux 命令有依赖性,我也很惊讶。我想可能是因为在安装 Ansible之后,它会在节点上显示一些信息。

检查未满足的依赖性

你可能会被 Ubuntu 中未满足的依赖问题所困扰,其他 Linux 也有类似问题。apt-cache 命令提供了一个选项来检查系统中各种可用软件包的所有未满足的依赖关系。

apt-cache unmet

结论

你可以用 apt-cache 命令列出所有可用的软件包。输出结果会很庞大,所以我建议将其与 wc 命令 结合起来,得到可用软件包的总数,就像这样:

apt-cache pkgnames | wc -l

你是否注意到你不需要成为 root 用户就可以使用 apt-cache 命令?

较新的 apt 命令也有一些与 apt-cache 命令相对应的功能选项。由于 apt 比较新,所以在脚本中还是首选使用 apt-get 及其相关的 apt-cache 等命令。

希望你觉得本教程对你有帮助。如果你对上面讨论的任何一点有疑问或者有改进的建议,请在评论中告诉我。


via: https://itsfoss.com/apt-cache-command/

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

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

树莓派是最著名的单板计算机。最初,树莓派项目的范围旨在促进学校和发展中国家的计算机基础科学的教学。

它的低成本、便携性和极低的功耗,使得它的受欢迎程度远远超过预期。从气象站到家庭自动化,玩家们用树莓派搭建了许多酷炫的项目

第四代树莓派具备了普通台式电脑的功能和处理能力。但本文并不是要介绍如何使用树莓派作为桌面。相反,我会告诉你如何在树莓派上安装 Ubuntu 服务器。

在本教程中,我将使用树莓派 4,以下是我将介绍的内容:

  • 在 microSD 卡上安装 Ubuntu 服务器
  • 在树莓派上设置无线网络连接
  • 通过 SSH 访问你的树莓派

本教程需要以下设备

  • 一张 micro SD 卡(建议使用 8GB 或更大的卡)
  • 一台带有 micro SD 卡读卡器的计算机(运行 Linux、Windows 或 macOS)
  • 树莓派 2、3 或 4
  • 良好的互联网连接
  • 用于树莓派 2 和 3 的 HDMI 线和用于树莓派 4 的 micro HDMI 线(可选)
  • 一套 USB 键盘(可选)

在树莓派上安装 Ubuntu 服务器

在本教程中,我使用 Ubuntu 来创建树莓派 SD 卡,但你可以在其他 Linux 发行版、macOS 和 Windows 上创建它。这是因为准备 SD 卡的步骤对 Raspberry Pi Imager 工具而言是一样的。

Raspberry Pi Imager 工具会自动下载你选择的树莓派系统镜像。这意味着你需要一个良好的网络连接来下载 1GB 左右的数据。

步骤 1:用 Raspberry Pi Imager 准备 SD 卡

确保你已将 microSD 卡插入电脑,并在电脑上安装 Raspberry Pi Imager。

你可以从这些链接中下载适合你操作系统的 Imager 工具:

尽管我使用的是 Ubuntu,但我不会使用上面列出的 Debian 软件包,而是使用命令行安装 snap 包。这个方法可以适用于更广泛的 Linux 发行版。

sudo snap install rpi-imager

安装好 Raspberry Pi Imager 工具后,找到并打开它,点击 “CHOOSE OS” 菜单。

滚动菜单并点击 “Ubuntu” (“核心”和“服务器”镜像)。

从可用的镜像中,我选择了 Ubuntu 20.04 LTS 64 位。如果你有一个树莓派 2,那你只能选择 32 位镜像。

重要提示:如果你使用的是最新的树莓派 4 - 8 GB 内存型号,你应该选择 64 位操作系统,否则只能使用 4 GB 内存。

从 “SD Card” 菜单中选择你的 microSD 卡,然后点击 “WRITE”。

如果它显示一些错误,请尝试再次写入它。现在它将下载 Ubuntu 服务器镜像并将其写入 micro SD 卡。

当这个过程完成时,它将通知你。

步骤 2:在 Ubuntu 服务器上添加 WiFi 支持

烧录完 micro SD 卡后,你就差不多可以使用它了。在使用它之前,有一件事情你可能想做,那就是添加 Wi-Fi 支持。

SD 卡仍然插入读卡器中,打开文件管理器,找到卡上的 “system-boot” 分区。

你要找的和需要编辑的文件名为 network-config

这个过程也可以在 Windows 和 MacOS 上完成。如前所述,编辑 network-config 文件,添加你的 Wi-Fi 凭证。

首先,取消矩形框内的行的注释(删除开头的标签 #)。

之后,将 myhomewifi 替换为你的 Wi-Fi 网络名,比如 "itsfoss",将 "S3kr1t" 替换为 Wi-Fi 密码,用引号括起来,比如 "12345679"

它可能看上去像这样:

wifis:
  wlan0:
    dhcp4: true
    optional: true
    access-points:
      "your wifi name":
      password: "your_wifi_password"

保存文件并将 micro SD 卡插入到你的树莓派中。在第一次启动时,如果你的树莓派无法连接到 Wi-Fi 网络,只需重启你的设备。

步骤 3:在树莓派上使用 Ubuntu 服务器(如果你有专门的显示器、键盘和鼠标的话)

如果你有一套额外的鼠标,键盘和显示器,你可以很容易地像其他电脑一样使用树莓派(但没有 GUI)。

只需将 micro SD 卡插入树莓派,连接显示器、键盘和鼠标。现在打开你的树莓派。它将出现 TTY 登录屏幕(黑色终端屏幕)并询问用户名和密码。

  • 默认用户名:ubuntu
  • 默认密码:ubuntu

看到提示符时,用 ubuntu 作为密码。登录成功后,Ubuntu 会要求你更改默认密码

享受你的 Ubuntu 服务器吧!

步骤 3:通过 SSH 远程连接到你的树莓派(如果你没有树莓派的显示器、键盘和鼠标的话)

如果你没有专门与树莓派一起使用的显示器也没关系。当你可以直接通过 SSH 进入它并按照你的方式使用它时,谁还需要一个带有显示器的服务器呢?

在 Ubuntu 和 Mac OS上,通常已经安装了一个 SSH 客户端。要远程连接到你的树莓派,你需要找到它的 IP 地址。检查连接到你的网络的设备,看看哪个是树莓派。

由于我没有 Windows 机器,你可以访问微软提供的综合指南。

打开终端,运行以下命令:

ssh ubuntu@raspberry_pi_ip_address

你可能会看到以下信息确认连接:

Are you sure you want to continue connecting (yes/no/[fingerprint])?

输入 yes,然后点击回车键。

当提示时,用前面提到的 ubuntu 作为密码。当然,你会被要求更改密码。

完成后,你将自动注销,你必须使用新密码重新连接。

你的 Ubuntu 服务器就可以在树莓派上运行了!

总结

在树莓派上安装 Ubuntu 服务器是一个简单的过程,而且它的预配置程度很高,使用起来很愉快。

我不得不说,在所有我在树莓派上尝试的操作系统中,Ubuntu 服务器是最容易安装的。我并没有夸大其词。请查看我的在树莓派上安装 Arch Linux 的指南,以供参考。

希望这篇指南也能帮助你在树莓派上安装 Ubuntu 服务器。如果你有问题或建议,请在评论区告诉我。


via: https://itsfoss.com/install-ubuntu-server-raspberry-pi/

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

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

在本系列的 第一第二 部分中讨论的大多数示例都是以某种方式闪烁的 LED。起初它可能很有趣,但是一段时间后变得有些无聊。让我们做些更有趣的事情……

…让我们点亮更多的 LED!

STM32F030F4P6

WS281x LED

WS281x RGB LED(及其克隆品)非常受欢迎。你可以以单个元素购买、链成长条或组装成矩阵、环或其他形状。

WS2812B

它们可以串联连接,基于这个事实,你可以只用 MCU 的单个引脚就可以控制一个很长的 LED 灯条。不幸的是,它们的内部控制器使用的物理协议不能直接适用于你在 MCU 中可以找到的任何外围设备。你必须使用 位脉冲 bit-banging 或以特殊方式使用可用的外设。

哪种可用的解决方案最有效取决于同时控制的 LED 灯条数量。如果你必须驱动 4 到 16 个灯条,那么最有效的方法是 使用定时器和 DMA(请不要忽略这篇文章末尾的链接)。

如果只需要控制一个或两个灯条,请使用可用的 SPI 或 UART 外设。对于 SPI,你只能在发送的一个字节中编码两个 WS281x 位。由于巧妙地使用了起始位和停止位,UART 允许更密集的编码:每发送一个字节 3 位。

我在 此站点 上找到了有关 UART 协议如何适用于 WS281x 协议的最佳解释。如果你不懂波兰语,这里是 英文翻译

基于 WS281x 的 LED 仍然是最受欢迎的,但市场上也有 SPI 控制的 LED:APA102SK9822。关于它们的三篇有趣的文章在这里:123

LED 环

市场上有许多基于 WS2812 的环。我有一个这样的:

WS2812B

它具有 24 个可单独寻址的 RGB LED(WS2812B),并暴露出四个端子:GND、5V、DI 和 DO。通过将 DI(数据输入)端子连接到上一个的 DO(数据输出)端子,可以链接更多的环或其他基于 WS2812 的东西。

让我们将这个环连接到我们的 STM32F030 板上。我们将使用基于 UART 的驱动程序,因此 DI 应连接到 UART 接头连接器上的 TXD 引脚。 WS2812B LED 需要至少 3.5V 的电源。 24 个 LED 会消耗大量电流,因此在编程/调试期间,最好将环上的 GND 和 5V 端子直接连接到 ST-LINK 编程器上可用的 GND 和 5V 引脚:

WS2812B

我们的 STM32F030F4P6 MCU 和整个 STM32 F0、F3、F7、L4 系列具有 F1、F4、L1 MCU 不具备的一项重要功能:它可以反转 UART 信号,因此我们可以将环直接连接到 UART TXD 引脚。如果你不知道我们需要这种反转,那么你可能没有读过我上面提到的 文章

因此,你不能以这种方式使用流行的 Blue PillSTM32F4-DISCOVERY。使用其 SPI 外设或外部反相器。有关使用 SPI 的 NUCLEO-F411RE,请参见 圣诞树灯 项目作为 UART + 逆变器的示例或 WS2812示例

顺便说一下,大多数 DISCOVERY 板可能还有一个问题:它们在 VDD = 3V 而不是 3.3V 的情况下工作。 对于高 DI,WS281x 至少要求电源电压 * 0.7。如果是 5V 电源,则为 3.5V;如果是 4.7V 电源,则为 3.3V;可在 DISCOVERY 的 5V 引脚上找到。如你所见,即使在我们的情况下,第一个 LED 的工作电压也低于规格 0.2V。对于 DISCOVERY 板,如果供电 4.7V,它将工作在低于规格的 0.3V 下;如果供电 5V,它将工作在低于规格 0.5V 下。

让我们结束这段冗长的介绍并转到代码:

package main

import (
    "delay"
    "math/rand"
    "rtos"

    "led"
    "led/ws281x/wsuart"

    "stm32/hal/dma"
    "stm32/hal/gpio"
    "stm32/hal/irq"
    "stm32/hal/system"
    "stm32/hal/system/timer/systick"
    "stm32/hal/usart"
)

var tts *usart.Driver

func init() {
    system.SetupPLL(8, 1, 48/8)
    systick.Setup(2e6)

    gpio.A.EnableClock(true)
    tx := gpio.A.Pin(9)

    tx.Setup(&gpio.Config{Mode: gpio.Alt})
    tx.SetAltFunc(gpio.USART1_AF1)

    d := dma.DMA1
    d.EnableClock(true)

    tts = usart.NewDriver(usart.USART1, d.Channel(2, 0), nil, nil)
    tts.Periph().EnableClock(true)
    tts.Periph().SetBaudRate(3000000000 / 1390)
    tts.Periph().SetConf2(usart.TxInv)
    tts.Periph().Enable()
    tts.EnableTx()

    rtos.IRQ(irq.USART1).Enable()
    rtos.IRQ(irq.DMA1_Channel2_3).Enable()
}

func main() {
    var rnd rand.XorShift64
    rnd.Seed(1)
    rgb := wsuart.GRB
    strip := wsuart.Make(24)
    black := rgb.Pixel(0)
    for {
        c := led.Color(rnd.Uint32()).Scale(127)
        pixel := rgb.Pixel(c)
        for i := range strip {
            strip[i] = pixel
            tts.Write(strip.Bytes())
            delay.Millisec(40)
        }
        for i := range strip {
            strip[i] = black
            tts.Write(strip.Bytes())
            delay.Millisec(20)
        }
    }
}

func ttsISR() {
    tts.ISR()
}

func ttsDMAISR() {
    tts.TxDMAISR()
}

//c:__attribute__((section(".ISRs")))
var ISRs = [...]func(){
    irq.USART1:          ttsISR,
    irq.DMA1_Channel2_3: ttsDMAISR,
}

导入部分

与前面的示例相比,导入部分中的新内容是 rand/math 包和带有 led/ws281x 子树的 led 包。 led 包本身包含 Color 类型的定义。 led/ws281x/wsuart 定义了 ColorOrderPixelStrip 类型。

我想知道如何使用 image/color 中的 ColorRGBA 类型,以及如何以它将实现 image.Image 接口的方式定义 Strip。 但是由于使用了 gamma 校正 和 大开销的 color/draw 包,我以简单的方式结束:

type Color uint32
type Strip []Pixel

使用一些有用的方法。然而,这种情况在未来可能会改变。

init 函数

init 函数没有太多新颖之处。 UART 波特率从 115200 更改为 3000000000/1390 ≈ 2158273,相当于每个 WS2812 位 1390 纳秒。 CR2 寄存器中的 TxInv 位设置为反转 TXD 信号。

main 函数

XorShift64 伪随机数生成器用于生成随机颜色。 XORSHIFT 是目前由 math/rand 包实现的唯一算法。你必须使用带有非零参数的 Seed 方法显式初始化它。

rgb 变量的类型为 wsuart.ColorOrder,并设置为 WS2812 使用的 GRB 颜色顺序(WS2811 使用 RGB 顺序)。然后用于将颜色转换为像素。

wsuart.Make(24) 创建 24 像素的初始化条带。它等效于:

strip := make(wsuart.Strip, 24)
strip.Clear()

其余代码使用随机颜色绘制类似于 “Please Wait…” 微调器的内容。

strip 切片充当帧缓冲区。 tts.Write(strip.Bytes()) 将帧缓冲区的内容发送到环。

中断

该程序由处理中断的代码组成,与先前的 UART 示例 中的代码相同。

让我们编译并运行:

$ egc
$ arm-none-eabi-size cortexm0.elf
   text    data     bss     dec     hex filename
  14088     240     204   14532    38c4 cortexm0.elf
$ openocd -d0 -f interface/stlink.cfg -f target/stm32f0x.cfg -c 'init; program cortexm0.elf; reset run; exit'

我跳过了 openocd 的输出。下面的视频显示了该程序的工作原理:

让我们做些有用的事情...

第一部分 的开头,我曾问过:“Go 能深入到多低层,而还能做一些有用的事情?”。 我们的 MCU 实际上是一种低端设备(8 比特的人可能会不同意我的看法),但到目前为止,我们还没有做任何有用的事情。

所以... 让我们做些有用的事情... 让我们做个时钟!

在互联网上有许多由 RGB LED 构成的时钟示例。让我们用我们的小板子和 RGB 环制作自己的时钟。我们按照下面的描述更改先前的代码。

导入部分

删除 math/rand 包,然后添加 stm32/hal/exti

全局变量

添加两个新的全局变量:btnbtnev

var (
    tts   *usart.Driver
    btn   gpio.Pin
    btnev rtos.EventFlag
)

它们将用来处理那些用于设置时钟的 “按钮”。我们的板子除了重置之外没有其他按钮,但是如果没有它,我们仍然可以通过某种方式进行管理。

init 函数

将这段代码添加到 init 函数:

btn = gpio.A.Pin(4)

btn.Setup(&gpio.Config{Mode: gpio.In, Pull: gpio.PullUp})
ei := exti.Lines(btn.Mask())
ei.Connect(btn.Port())
ei.EnableFallTrig()
ei.EnableRiseTrig()
ei.EnableIRQ()

rtos.IRQ(irq.EXTI4_15).Enable()

在内部 上拉电阻 pull-up resistor 启用的情况下,将 PA4 引脚配置为输入。它已连接至板载 LED,但这不会妨碍任何事情。更重要的是它位于 GND 引脚旁边,所以我们可以使用任何金属物体来模拟按钮并设置时钟。作为奖励,我们还有来自板载 LED 的其他反馈。

我们使用 EXTI 外设来跟踪 PA4 状态。它被配置为在发生任何更改时都会产生中断。

btnWait 函数

定义一个新的辅助函数:

func btnWait(state int, deadline int64) bool {
    for btn.Load() != state {
        if !btnev.Wait(1, deadline) {
            return false // timeout
        }
        btnev.Reset(0)
    }
    delay.Millisec(50) // debouncing
    return true
}

它等待 “按钮” 引脚上的指定状态,但只等到最后期限出现。这是稍微改进的轮询代码:

for btn.Load() != state {
    if rtos.Nanosec() >= deadline {
        // timeout
    }
}

我们的 btnWait 函数不是忙于等待 statedeadline,而是使用 rtos.EventFlag 类型的 btnev 变量休眠,直到有事情发生。你当然可以使用通道而不是 rtos.EventFlag,但是后者便宜得多。

main 函数

我们需要全新的 main 函数:

func main() {
    rgb := wsuart.GRB
    strip := wsuart.Make(24)
    ds := 4 * 60 / len(strip) // Interval between LEDs (quarter-seconds).
    adjust := 0
    adjspeed := ds
    for {
        qs := int(rtos.Nanosec() / 25e7) // Quarter-seconds since reset.
        qa := qs + adjust

        qa %= 12 * 3600 * 4 // Quarter-seconds since 0:00 or 12:00.
        hi := len(strip) * qa / (12 * 3600 * 4)

        qa %= 3600 * 4 // Quarter-seconds in the current hour.
        mi := len(strip) * qa / (3600 * 4)

        qa %= 60 * 4 // Quarter-seconds in the current minute.
        si := len(strip) * qa / (60 * 4)

        hc := led.Color(0x550000)
        mc := led.Color(0x005500)
        sc := led.Color(0x000055)

        // Blend the colors if the hands of the clock overlap.
        if hi == mi {
            hc |= mc
            mc = hc
        }
        if mi == si {
            mc |= sc
            sc = mc
        }
        if si == hi {
            sc |= hc
            hc = sc
        }

        // Draw the clock and write to the ring.
        strip.Clear()
        strip[hi] = rgb.Pixel(hc)
        strip[mi] = rgb.Pixel(mc)
        strip[si] = rgb.Pixel(sc)
        tts.Write(strip.Bytes())

        // Sleep until the button pressed or the second hand should be moved.
        if btnWait(0, int64(qs+ds)*25e7) {
            adjust += adjspeed
            // Sleep until the button is released or timeout.
            if !btnWait(1, rtos.Nanosec()+100e6) {
                if adjspeed < 5*60*4 {
                    adjspeed += 2 * ds
                }
                continue
            }
            adjspeed = ds
        }
    }
}

我们使用 rtos.Nanosec 函数代替 time.Now 来获取当前时间。这样可以节省大量的闪存,但也使我们的时钟变成了不知道日、月、年的老式设备,最糟糕的是它无法处理夏令时的变化。

我们的环有 24 个 LED,因此秒针的显示精度可以达到 2.5 秒。为了不牺牲这种精度并获得流畅的运行效果,我们使用 1/4 秒作为基准间隔。半秒就足够了,但四分之一秒更准确,而且与 16 和 48 个 LED 配合使用也很好。

红色、绿色和蓝色分别用于时针、分针和秒针。这允许我们使用简单的“逻辑或操作”进行颜色混合。我们 Color.Blend 方法可以混合任意颜色,但是我们闪存不多,所以我们选择最简单的解决方案。

我们只有在秒针移动时才重画时钟。

btnWait(0, int64(qs+ds)*25e7)

上面的这行代码等待的正是那一刻,或者是按钮的按下。

每按一下按钮就会把时钟向前调一调。按住按钮一段时间会加速调整。

中断

定义新的中断处理程序:

func exti4_15ISR() {
    pending := exti.Pending() & 0xFFF0
    pending.ClearPending()
    if pending&exti.Lines(btn.Mask()) != 0 {
        btnev.Signal(1)
    }
}

并将 irq.EXTI4_15: exti4_15ISR 条目添加到 ISR 数组。

该处理程序(或中断服务程序)处理 EXTI4\_15 IRQ。 Cortex-M0 CPU 支持的 IRQ 明显少于其较大的同类兄弟处理器,因此你经常可以看到一个 IRQ 被多个中断源共享。在我们的例子中,一个 IRQ 由 12 个 EXTI 线共享。

exti4\_15ISR 读取所有挂起的位,并从中选择 12 个更高的有效位。接下来,它清除 EXTI 中选中的位并开始处理它们。在我们的例子中,仅检查第 4 位。 btnev.Signal(1) 引发 btnev.Wait(1, deadline) 唤醒并返回 true

你可以在 Github 上找到完整的代码。让我们来编译它:

$ egc
$ arm-none-eabi-size cortexm0.elf
   text    data     bss     dec     hex filename
  15960     240     216   16416    4020 cortexm0.elf

这里所有的改进只得到 184 个字节。让我们再次重新构建所有内容,但这次在 typeinfo 中不使用任何类型和字段名:

$ cd $HOME/emgo
$ ./clean.sh
$ cd $HOME/firstemgo
$ egc -nf -nt
$ arm-none-eabi-size cortexm0.elf
   text    data     bss     dec     hex filename
  15120     240     216   15576    3cd8 cortexm0.elf

现在,有了千字节的空闲空间,你可以改进一些东西。让我们看看它是如何工作的:

我不知道我是怎么精确打到 3:00 的!?

以上就是所有内容!在第 4 部分(本系列的结束)中,我们将尝试在 LCD 上显示一些内容。(LCTT 译注:然而烂尾了,第三篇写于 2018 年,整个博客当年就停更了。)


via: https://ziutek.github.io/2018/05/03/go_on_very_small_hardware3.html

作者:Michał Derkacz 选题:lujun9972 译者:gxlct008 校对:wxy

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

前几天,我收到一个警告,/boot 分区已经几乎满了,没有剩余空间了。是的,我有一个独立的 /boot 分区,我相信现在很少有人这样做了。(LCTT 译注:个人认为保留单独的 /boot 分区是个好的运维经验,除此以外,/tmp、/var 也单独划定分区比较好。)

这是我第一次看到这样一个错误,它让我很迷惑。现在,这里有一些 方法来释放在 Ubuntu (或基于 Ubuntu 的分区)上的分区 ,但是在这种情况下并不是所有的方法都能用。

这就是为什么我决定写这些我释放 /boot 分区空间的步骤的原因。

如何在 Ubuntu 上释放 /boot 分区的空间

我建议你仔细阅读这些解决方案,并由此得出最适合你情况的解决方案。解决方案的操作很容易,但是你需要在你的生产力系统上小心的执行这些解决方案。

方法 1: 使用 apt autoremove

你不必是一名终端专家来做这件事,它只需要一个命令,你将移除未使用的内核来释放 /boot 分区中是空间。

你所有要做的事情是,输入:

sudo apt autoremove

这个命令不仅仅可以移除未使用的内核,而且也将移除你不需要的或工具安装后所不需要的依赖项。

在你输入命令后,它将列出将被移除的东西,你只需要确认操作即可。如果你很好奇它将移除什么,你可以仔细检查一下看看它实际移除了什么。

这里是它应该看起来的样子:

你必须按 Y 按键来继续。

值得注意的是,这种方法只在你还剩余一点点空间,并且得到警告的情况下才有效。但是,如果你的 /boot 分区已经满了,APT 甚至可能不会工作。

在接下来的方法中,我将重点介绍两种不同的方法,通过这些方法你可以使用 GUI 和终端来移除旧内核来释放空间。

方法 2: 手动移除未使用的内核

在你尝试 移除一些旧内核 来释放空间前,你需要识别当前活动的内核,并且确保你不会删除它。

检查你的内核的版本 ,在终端中输入下面的命令:

uname -r

uname 命令通常用于获取 Linux 系统信息。在这里,这个命令显示当前正在被使用的 Linux 内核。它看起来应该是这样:

现在,你已经知道你当前的 Linux 内核是什么,你必须移除一个不同于这个版本的内核。你应该把它记录在某些地方,以便你不会不知不觉地移除它。

接下来,要移除它,你可以使用终端或 GUI。

警告!

在删除内核时一定要额外的小心。只识别和删除旧内核,而不是当前你正在使用的内核,否则你将会拥有一个残缺的系统。

使用一个 GUI 工具来移除旧的 Linux 内核

你可以使用 Synaptic 软件包管理器 或一个类似 Stacer 的工具来开始。就我个人而言,当我遇到一个满满的 /boot 分区且 APT 损坏时,我使用 Stacer 来丢弃旧内核。因此,让我向你展示一下它看起的样子。

首先,你需要启动 Stacer ,然后导航到软件包卸载器,如下面屏幕截图所示。

在这里,搜索 “image” ,你将找到你所拥有的 Linux 内核。你只需要删除旧内核版本的镜像,而不是当前内核的镜像。

在上面的屏幕截图中,我已经指出了我系统上的当前内核和旧内核,因此你必须注意你系统上的内核。

你没有必要删除任何其它东西,只需要删除旧的内核版本。

同样的,只需要在软件包列表中搜索 “headers” ,并删除如下显示的旧的 “headers” 版本。

作为提醒,你 不会希望移除 linux-headers-generic 。只关注一下那些与其相关的有版本号的就行。

然后,就这样,你完成了所有的工作,APT 将会再次工作,并且你将成功地释放来自 /boot 分区的一些空间。同样地,你也可以使用任意其它的软件包管理器来完成这些工作。

使用命令行来移除旧内核

使用命令行来移除旧内核与使用 GUI 来移除旧内核是一样的。因此,如果你没有选择使用 GUI 软件(如果它是一台远程机器/一项远程服务)的权利,或者如果你只是对终端情有独钟,你可以仿效下面的步骤。

首先,使用下面的命令列出所有已安装的内核:

ls -l /boot

它应该看起来像这样:

标记为 “old” 的内核,或者不匹配你当前内核版本,都是未使用的内核,你可以删除它们。

现在,你可以使用 rm 命令来移除具体指定来自 /boot 分区中的内核,使用下面的命令(一个命令对应一个内核):

sudo rm /boot/vmlinuz-5.4.0-7634-generic

务必检查系统的版本 — 这里可能与你的系统的版本不同。

如果你有很多未使用的内核,这将需要一些时间。因此,你也可以下面的命令丢弃多个内核:

sudo rm /boot/*-5.4.0-{7634}-*

为了清晰起见,你需要用逗号分隔内核版本号的最后一部分/编码,以便同时删除它们。

假设,我有两个旧的内核 5.4.0-7634-generic 和 5.4.0-7624 ,那么命令将是:

sudo rm /boot/*-5.4.0-{7634,7624}-*

如果你不希望在 grub 启动菜单中再看到这些旧的内核版本,你可以使用下面的命令简单地 更新 grub

sudo update-grub

就这样,你完成了所有的工作。你已经释放了空间,还修复了可能潜在的破损的 APT 问题,如果它是一个在你的 /boot 分区填满后出现的重要的问题的话。

在一些情况下,你需要输入这些命令来修复破损的(正如我在论坛中注意到的):

sudo dpkg --configure -a
sudo apt install -f

注意,除非你发现 APT 已破损,否则你不需要输入上面的命令。就我个人而言,我不需要这些命令,但是我发现这些命令对论坛上的一些人很有用。


via: https://itsfoss.com/free-boot-partition-ubuntu/

作者:Ankush Das 选题:lujun9972 译者:robsean 校对:wxy

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