分类 技术 下的文章

Rust 通常被称为 rust-lang。Rust 是一个由 Mozilla Research 赞助的通用的、多范式、现代的、跨平台和开源系统编程语言。

它旨在实现安全性、速度和并发性等目标。

Rust 在语法上与 C++ 相似,但它的设计者希望它在保持性能的同时提供更好的内存安全性。

Rust 目前在许多组织中使用,例如 Firefox、Chef、Dropbox、Oracle、GNOME 等。

如何在 Linux 中安装 Rust 语言?

我们可以通过多种方式安装 Rust,但以下是官方推荐的安装方式。

$ curl https://sh.rustup.rs -sSf | sh
info: downloading installer

Welcome to Rust!

This will download and install the official compiler for the Rust programming 
language, and its package manager, Cargo.

It will add the cargo, rustc, rustup and other commands to Cargo's bin 
directory, located at:

  /home/daygeek/.cargo/bin

This path will then be added to your PATH environment variable by modifying the
profile files located at:

  /home/daygeek/.profile
  /home/daygeek/.bash_profile

You can uninstall at any time with rustup self uninstall and these changes will
be reverted.

Current installation options:

   default host triple: x86_64-unknown-linux-gnu
     default toolchain: stable
  modify PATH variable: yes

1) Proceed with installation (default)
2) Customize installation
3) Cancel installation
>1

info: syncing channel updates for 'stable-x86_64-unknown-linux-gnu'
info: latest update on 2018-12-06, rust version 1.31.0 (abe02cefd 2018-12-04)
info: downloading component 'rustc'
 77.7 MiB /  77.7 MiB (100 %)   1.2 MiB/s ETA:   0 s                
info: downloading component 'rust-std'
 54.2 MiB /  54.2 MiB (100 %)   1.2 MiB/s ETA:   0 s                
info: downloading component 'cargo'
  4.7 MiB /   4.7 MiB (100 %)   1.2 MiB/s ETA:   0 s                
info: downloading component 'rust-docs'
  8.5 MiB /   8.5 MiB (100 %)   1.2 MiB/s ETA:   0 s                
info: installing component 'rustc'
info: installing component 'rust-std'
info: installing component 'cargo'
info: installing component 'rust-docs'
info: default toolchain set to 'stable'

  stable installed - rustc 1.31.0 (abe02cefd 2018-12-04)


Rust is installed now. Great!

To get started you need Cargo's bin directory ($HOME/.cargo/bin) in your PATH 
environment variable. Next time you log in this will be done automatically.

To configure your current shell run source $HOME/.cargo/env

运行以下命令配置当前 shell。

$ source $HOME/.cargo/env

运行以下命令验证已安装的 Rust 版本。

$ rustc --version
rustc 1.31.0 (abe02cefd 2018-12-04)

如何测试 Rust 编程语言?

安装 Rust 后,请按照以下步骤检查 Rust 语言是否正常工作。

$ mkdir ~/projects
$ cd ~/projects
$ mkdir hello_world
$ cd hello_world

创建一个文件并添加以下代码并保存。确保 Rust 文件始终以 .rs 扩展名结尾。

$ vi 2g.rs

fn main() {
 println!("Hello, It's 2DayGeek.com - Best Linux Practical Blog!");
}

运行以下命令编译 rust 代码。

$ rustc 2g.rs

上面的命令将在同一目录中创建一个可执行的 Rust 程序。

$ ls -lh
total 3.9M
-rwxr-xr-x 1 daygeek daygeek 3.9M Dec 14 11:09 2g
-rw-r--r-- 1 daygeek daygeek 86 Dec 14 11:09 2g.rs

运行 Rust 可执行文件得到输出。

$ ./2g
Hello, It's 2DayGeek.com - Best Linux Practical Blog!

好了!正常工作了。

将 Rust 更新到最新版本。

$ rustup update
info: syncing channel updates for 'stable-x86_64-unknown-linux-gnu'
info: checking for self-updates

  stable-x86_64-unknown-linux-gnu unchanged - rustc 1.31.0 (abe02cefd 2018-12-04)

运行以下命令从系统中删除 Rust 包。

$ rustup self uninstall

卸载 Rust 包后,删除 Rust 项目目录。

$ rm -fr ~/projects

via: https://www.2daygeek.com/how-to-install-rust-programming-language-in-linux/

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

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

简介

在本实验中,你将要去实现 spawn,它是一个加载和运行磁盘上可运行文件的库调用。然后,你接着要去充实你的内核和库,以使操作系统能够在控制台上运行一个 shell。而这些特性需要一个文件系统,本实验将引入一个可读/写的简单文件系统。

预备知识

使用 Git 去获取最新版的课程仓库,然后创建一个命名为 lab5 的本地分支,去跟踪远程的 origin/lab5 分支:

athena% cd ~/6.828/lab
athena% add git
athena% git pull
Already up-to-date.
athena% git checkout -b lab5 origin/lab5
Branch lab5 set up to track remote branch refs/remotes/origin/lab5.
Switched to a new branch "lab5"
athena% git merge lab4
Merge made by recursive.
.....
athena%

在实验中这一部分的主要新组件是文件系统环境,它位于新的 fs 目录下。通过检查这个目录中的所有文件,我们来看一下新的文件都有什么。另外,在 userlib 目录下还有一些文件系统相关的源文件。

  • fs/fs.c 维护文件系统在磁盘上结构的代码
  • fs/bc.c 构建在我们的用户级页故障处理功能之上的一个简单的块缓存
  • fs/ide.c 极简的基于 PIO(非中断驱动的)IDE 驱动程序代码
  • fs/serv.c 使用文件系统 IPC 与客户端环境交互的文件系统服务器
  • lib/fd.c 实现一个常见的类 UNIX 的文件描述符接口的代码
  • lib/file.c 磁盘上文件类型的驱动,实现为一个文件系统 IPC 客户端
  • lib/console.c 控制台输入/输出文件类型的驱动
  • lib/spawn.c spawn 库调用的框架代码

你应该再次去运行 pingpongprimesforktree,测试实验 4 完成后合并到新的实验 5 中的代码能否正确运行。你还需要在 kern/init.c 中注释掉 ENV_CREATE(fs_fs) 行,因为 fs/fs.c 将尝试去做一些 I/O,而 JOS 到目前为止还不具备该功能。同样,在 lib/exit.c 中临时注释掉对 close_all() 的调用;这个函数将调用你在本实验后面部分去实现的子程序,如果现在去调用,它将导致 JOS 内核崩溃。如果你的实验 4 的代码没有任何 bug,将很完美地通过这个测试。在它们都能正常工作之前是不能继续后续实验的。在你开始做练习 1 时,不要忘记去取消这些行上的注释。

如果它们不能正常工作,使用 git diff lab4 去重新评估所有的变更,确保你在实验 4(及以前)所写的代码在本实验中没有丢失。确保实验 4 仍然能正常工作。

实验要求

和以前一样,你需要做本实验中所描述的所有常规练习和至少一个挑战问题。另外,你需要写出你在本实验中问题的详细答案,和你是如何解决挑战问题的一个简短(即:用一到两个段落)的描述。如果你实现了多个挑战问题,你只需要写出其中一个即可,当然,我们欢迎你做的越多越好。在你动手实验之前,将你的问题答案写入到你的 lab5 根目录下的一个名为 answers-lab5.txt 的文件中。

文件系统的雏形

你将要使用的文件系统比起大多数“真正的”文件系统(包括 xv6 UNIX 的文件系统)要简单的多,但它也是很强大的,足够去提供基本的特性:创建、读取、写入和删除组织在层次目录结构中的文件。

到目前为止,我们开发的是一个单用户操作系统,它提供足够的保护并能去捕获 bug,但它还不能在多个不可信用户之间提供保护。因此,我们的文件系统还不支持 UNIX 的所有者或权限的概念。我们的文件系统目前也不支持硬链接、时间戳、或像大多数 UNIX 文件系统实现的那些特殊的设备文件。

磁盘上的文件系统结构

主流的 UNIX 文件系统将可用磁盘空间分为两种主要的区域类型:节点区域和数据区域。UNIX 文件系统在文件系统中为每个文件分配一个节点;一个文件的节点保存了这个文件重要的元数据,比如它的 stat 属性和指向数据块的指针。数据区域被分为更大的(一般是 8 KB 或更大)数据块,它在文件系统中存储文件数据和目录元数据。目录条目包含文件名字和指向到节点的指针;如果文件系统中的多个目录条目指向到那个文件的节点上,则称该文件是硬链接的。由于我们的文件系统不支持硬链接,所以我们不需要这种间接的级别,并且因此可以更方便简化:我们的文件系统将压根就不使用节点,而是简单地将文件的(或子目录的)所有元数据保存在描述那个文件的(唯一的)目录条目中。

文件和目录逻辑上都是由一系列的数据块组成,它或许是很稀疏地分散到磁盘上,就像一个环境的虚拟地址空间上的页,能够稀疏地分散在物理内存中一样。文件系统环境隐藏了块布局的细节,只提供文件中任意偏移位置读写字节序列的接口。作为像文件创建和删除操作的一部分,文件系统环境服务程序在目录内部完成所有的修改。我们的文件系统允许用户环境去直接读取目录元数据(即:使用 read),这意味着用户环境自己就能够执行目录扫描操作(即:实现 ls 程序),而不用另外依赖对文件系统的特定调用。用这种方法做目录扫描的缺点是,(也是大多数现代 UNIX 操作系统变体摒弃它的原因)使得应用程序依赖目录元数据的格式,如果不改变或至少要重编译应用程序的前提下,去改变文件系统的内部布局将变得很困难。

扇区和块

大多数磁盘都不能执行以字节为粒度的读写操作,而是以扇区为单位执行读写。在 JOS 中,每个扇区是 512 字节。文件系统实际上是以块为单位来分配和使用磁盘存储的。要注意这两个术语之间的区别:扇区大小是硬盘硬件的属性,而块大小是使用这个磁盘的操作系统上的术语。一个文件系统的块大小必须是底层磁盘的扇区大小的倍数。

UNIX xv6 文件系统使用 512 字节大小的块,与它底层磁盘的扇区大小一样。而大多数现代文件系统使用更大尺寸的块,因为现在存储空间变得很廉价了,而使用更大的粒度在存储管理上更高效。我们的文件系统将使用 4096 字节的块,以更方便地去匹配处理器上页的大小。

超级块

Disk layout

文件系统一般在磁盘上的“易于查找”的位置(比如磁盘开始或结束的位置)保留一些磁盘块,用于保存描述整个文件系统属性的元数据,比如块大小、磁盘大小、用于查找根目录的任何元数据、文件系统最后一次挂载的时间、文件系统最后一次错误检查的时间等等。这些特定的块被称为超级块。

我们的文件系统只有一个超级块,它固定为磁盘的 1 号块。它的布局定义在 inc/fs.h 文件里的 struct Super 中。而 0 号块一般是保留的,用于去保存引导加载程序和分区表,因此文件系统一般不会去使用磁盘上比较靠前的块。许多“真实的”文件系统都维护多个超级块,并将它们复制到间隔较大的几个区域中,这样即便其中一个超级块坏了或超级块所在的那个区域产生了介质错误,其它的超级块仍然能够被找到并用于去访问文件系统。

文件元数据

File structure

元数据的布局是描述在我们的文件系统中的一个文件中,这个文件就是 inc/fs.h 中的 struct File。元数据包含文件的名字、大小、类型(普通文件还是目录)、指向构成这个文件的块的指针。正如前面所提到的,我们的文件系统中并没有节点,因此元数据是保存在磁盘上的一个目录条目中,而不是像大多数“真正的”文件系统那样保存在节点中。为简单起见,我们将使用 File 这一个结构去表示文件元数据,因为它要同时出现在磁盘上和内存中。

struct File 中的数组 f_direct 包含一个保存文件的前 10 个块(NDIRECT)的块编号的空间,我们称之为文件的直接块。对于最大 10*4096 = 40KB 的小文件,这意味着这个文件的所有块的块编号将全部直接保存在结构 File 中,但是,对于超过 40 KB 大小的文件,我们需要一个地方去保存文件剩余的块编号。所以我们分配一个额外的磁盘块,我们称之为文件的间接块,由它去保存最多 4096/4 = 1024 个额外的块编号。因此,我们的文件系统最多允许有 1034 个块,或者说不能超过 4MB 大小。为支持大文件,“真正的”文件系统一般都支持两个或三个间接块。

目录与普通文件

我们的文件系统中的结构 File 既能够表示一个普通文件,也能够表示一个目录;这两种“文件”类型是由 File 结构中的 type 字段来区分的。除了文件系统根本就不需要解释的、分配给普通文件的数据块的内容之外,它使用完全相同的方式来管理普通文件和目录“文件”,文件系统将目录“文件”的内容解释为包含在目录中的一系列的由 File 结构所描述的文件和子目录。

在我们文件系统中的超级块包含一个结构 File(在 struct Super 中的 root 字段中),它用于保存文件系统的根目录的元数据。这个目录“文件”的内容是一系列的 File 结构所描述的、位于文件系统根目录中的文件和目录。在根目录中的任何子目录转而可以包含更多的 File 结构所表示的子目录,依此类推。

文件系统

本实验的目标并不是让你去实现完整的文件系统,你只需要去实现几个重要的组件即可。实践中,你将负责把块读入到块缓存中,并且刷新脏块到磁盘上;分配磁盘块;映射文件偏移量到磁盘块;以及实现读取、写入、和在 IPC 接口中打开。因为你并不去实现完整的文件系统,熟悉提供给你的代码和各种文件系统接口是非常重要的。

磁盘访问

我们的操作系统的文件系统环境需要能访问磁盘,但是我们在内核中并没有实现任何磁盘访问的功能。与传统的在内核中添加了 IDE 磁盘驱动程序、以及允许文件系统去访问它所必需的系统调用的“大一统”策略不同,我们将 IDE 磁盘驱动实现为用户级文件系统环境的一部分。我们仍然需要对内核做稍微的修改,是为了能够设置一些东西,以便于文件系统环境拥有实现磁盘访问本身所需的权限。

只要我们依赖轮询、基于 “编程的 I/O”(PIO)的磁盘访问,并且不使用磁盘中断,那么在用户空间中实现磁盘访问还是很容易的。也可以去实现由中断驱动的设备驱动程序(比如像 L3 和 L4 内核就是这么做的),但这样做的话,内核必须接收设备中断并将它派发到相应的用户模式环境上,这样实现的难度会更大。

x86 处理器在 EFLAGS 寄存器中使用 IOPL 位去确定保护模式中的代码是否允许执行特定的设备 I/O 指令,比如 INOUT 指令。由于我们需要的所有 IDE 磁盘寄存器都位于 x86 的 I/O 空间中而不是映射在内存中,所以,为了允许文件系统去访问这些寄存器,我们需要做的唯一的事情便是授予文件系统环境“I/O 权限”。实际上,在 EFLAGS 寄存器的 IOPL 位上规定,内核使用一个简单的“要么全都能访问、要么全都不能访问”的方法来控制用户模式中的代码能否访问 I/O 空间。在我们的案例中,我们希望文件系统环境能够去访问 I/O 空间,但我们又希望任何其它的环境完全不能访问 I/O 空间。

练习 1i386_init 通过将类型 ENV_TYPE_FS 传递给你的环境创建函数 env_create 来识别文件系统。修改 env.c 中的 env_create ,以便于它只授予文件系统环境 I/O 的权限,而不授予任何其它环境 I/O 的权限。

确保你能启动这个文件系统环境,而不会产生一般保护故障。你应该要通过在 make grade 中的 fs i/o 测试。

.

问题 1、当你从一个环境切换到另一个环境时,你是否需要做一些操作来确保 I/O 权限设置能被保存和正确地恢复?为什么?

注意本实验中的 GNUmakefile 文件,它用于设置 QEMU 去使用文件 obj/kern/kernel.img 作为磁盘 0 的镜像(一般情况下表示 DOS 或 Windows 中的 “C 盘”),以及使用(新)文件 obj/fs/fs.img 作为磁盘 1 的镜像(”D 盘“)。在本实验中,我们的文件系统应该仅与磁盘 1 有交互;而磁盘 0 仅用于去引导内核。如果你想去恢复其中一个有某些错误的磁盘镜像,你可以通过输入如下的命令,去重置它们到最初的、”崭新的“版本:

$ rm obj/kern/kernel.img obj/fs/fs.img
$ make

或者:

$ make clean
$ make

小挑战!实现中断驱动的 IDE 磁盘访问,既可以使用也可以不使用 DMA 模式。由你来决定是否将设备驱动移植进内核中、还是与文件系统一样保留在用户空间中、甚至是将它移植到一个它自己的的单独的环境中(如果你真的想了解微内核的本质的话)。

块缓存

在我们的文件系统中,我们将在处理器虚拟内存系统的帮助下,实现一个简单的”缓冲区“(实际上就是一个块缓冲区)。块缓存的代码在 fs/bc.c 文件中。

我们的文件系统将被限制为仅能处理 3GB 或更小的磁盘。我们保留一个大的、尺寸固定为 3GB 的文件系统环境的地址空间区域,从 0x10000000(DISKMAP)到 0xD0000000(DISKMAP+DISKMAX)作为一个磁盘的”内存映射版“。比如,磁盘的 0 号块被映射到虚拟地址 0x10000000 处,磁盘的 1 号块被映射到虚拟地址 0x10001000 处,依此类推。在 fs/bc.c 中的 diskaddr 函数实现从磁盘块编号到虚拟地址的转换(以及一些完整性检查)。

由于我们的文件系统环境在系统中有独立于所有其它环境的虚拟地址空间之外的、它自己的虚拟地址空间,并且文件系统环境仅需要做的事情就是实现文件访问,以这种方式去保留大多数文件系统环境的地址空间是很明智的。如果在一台 32 位机器上的”真实的“文件系统上这么做是很不方便的,因为现在的磁盘都远大于 3 GB。而在一台有 64 位地址空间的机器上,这样的缓存管理方法仍然是明智的。

当然,将整个磁盘读入到内存中需要很长时间,因此,我们将它实现成”按需“分页的形式,那样我们只在磁盘映射区域中分配页,并且当在这个区域中产生页故障时,从磁盘读取相关的块去响应这个页故障。通过这种方式,我们能够假装将整个磁盘装进了内存中。

练习 2、在 fs/bc.c 中实现 bc_pgfaultflush_block 函数。bc_pgfault 函数是一个页故障服务程序,就像你在前一个实验中编写的写时复制 fork 一样,只不过它的任务是从磁盘中加载页去响应一个页故障。在你编写它时,记住: (1) addr 可能并不会做边界对齐,并且 (2) 在扇区中的 ide_read 操作并不是以块为单位的。

(如果需要的话)函数 flush_block 应该会将一个块写入到磁盘上。如果在块缓存中没有块(也就是说,页没有映射)或者它不是一个脏块,那么 flush_block 将什么都不做。我们将使用虚拟内存硬件去跟踪,磁盘块自最后一次从磁盘读取或写入到磁盘之后是否被修改过。查看一个块是否需要写入时,我们只需要去查看 uvpt 条目中的 PTE_D 的 ”dirty“ 位即可。(PTE_D 位由处理器设置,用于表示那个页被写入;具体细节可以查看 x386 参考手册的 第 5 章 的 5.2.4.3 节)块被写入到磁盘上之后,flush_block 函数将使用 sys_page_map 去清除 PTE_D 位。

使用 make grade 去测试你的代码。你的代码应该能够通过 check\_bc、check\_super、和 check\_bitmap 的测试。

fs/fs.c 中的函数 fs_init 是块缓存使用的一个很好的示例。在初始化块缓存之后,它简单地在全局变量 super 中保存指针到磁盘映射区。在这之后,如果块在内存中,或我们的页故障服务程序按需将它们从磁盘上读入后,我们就能够简单地从 super 结构中读取块了。

.

小挑战!到现在为止,块缓存还没有清除策略。一旦某个块因为页故障被读入到缓存中之后,它将一直不会被清除,并且永远保留在内存中。给块缓存增加一个清除策略。在页表中使用 PTE_A 的 accessed 位来实现,任何环境访问一个页时,硬件就会设置这个位,你可以通过它来跟踪磁盘块的大致使用情况,而不需要修改访问磁盘映射区域的任何代码。使用脏块要小心。

块位图

fs_init 设置了 bitmap 指针之后,我们可以认为 bitmap 是一个装满比特位的数组,磁盘上的每个块就是数组中的其中一个比特位。比如 block_is_free,它只是简单地在位图中检查给定的块是否被标记为空闲。

练习 3、使用 free_block 作为实现 fs/fs.c 中的 alloc_block 的一个模型,它将在位图中去查找一个空闲的磁盘块,并将它标记为已使用,然后返回块编号。当你分配一个块时,你应该立即使用 flush_block 将已改变的位图块刷新到磁盘上,以确保文件系统的一致性。

使用 make grade 去测试你的代码。现在,你的代码应该要通过 alloc\_block 的测试。

文件操作

fs/fs.c 中,我们提供一系列的函数去实现基本的功能,比如,你将需要去理解和管理结构 File、扫描和管理目录”文件“的条目、 以及从根目录开始遍历文件系统以解析一个绝对路径名。阅读 fs/fs.c 中的所有代码,并在你开始实验之前,确保你理解了每个函数的功能。

练习 4、实现 file_block_walkfile_get_blockfile_block_walk 从一个文件中的块偏移量映射到 struct File 中那个块的指针上或间接块上,它非常类似于 pgdir_walk 在页表上所做的事。file_get_block 将更进一步,将去映射一个真实的磁盘块,如果需要的话,去分配一个新的磁盘块。

使用 make grade 去测试你的代码。你的代码应该要通过 file\_open、filegetblock、以及 fileflush/filetruncated/file rewrite、和 testfile 的测试。

file_block_walkfile_get_block 是文件系统中的”劳动模范“。比如,file_readfile_write 或多或少都在 file_get_block 上做必需的登记工作,然后在分散的块和连续的缓存之间复制字节。

.

小挑战!如果操作在中途实然被打断(比如,突然崩溃或重启),文件系统很可能会产生错误。实现软件更新或日志处理的方式让文件系统的”崩溃可靠性“更好,并且演示一下旧的文件系统可能会崩溃,而你的更新后的文件系统不会崩溃的情况。

文件系统接口

现在,我们已经有了文件系统环境自身所需的功能了,我们必须让其它希望使用文件系统的环境能够访问它。由于其它环境并不能直接调用文件系统环境中的函数,我们必须通过一个远程过程调用或 RPC、构建在 JOS 的 IPC 机制之上的抽象化来暴露对文件系统的访问。如下图所示,下图是对文件系统服务调用(比如:读取)的样子:

      Regular env           FS env
   +---------------+   +---------------+
   |      read     |   |   file_read   |
   |   (lib/fd.c)  |   |   (fs/fs.c)   |
...|.......|.......|...|.......^.......|...............
   |       v       |   |       |       | RPC mechanism
   |  devfile_read |   |  serve_read   |
   |  (lib/file.c) |   |  (fs/serv.c)  |
   |       |       |   |       ^       |
   |       v       |   |       |       |
   |     fsipc     |   |     serve     |
   |  (lib/file.c) |   |  (fs/serv.c)  |
   |       |       |   |       ^       |
   |       v       |   |       |       |
   |   ipc_send    |   |   ipc_recv    |
   |       |       |   |       ^       |
   +-------|-------+   +-------|-------+
           |                   |
           +-------------------+

圆点虚线下面的过程是一个普通的环境对文件系统环境请求进行读取的简单机制。从(我们提供的)在任何文件描述符上的 read 工作开始,并简单地派发到相关的设备读取函数上,在我们的案例中是 devfile_read(我们还有更多的设备类型,比如管道)。devfile_read 实现了对磁盘上文件指定的 read。它和 lib/file.c 中的其它的 devfile_* 函数实现了客户端侧的文件系统操作,并且所有的工作大致都是以相同的方式来完成的,把参数打包进一个请求结构中,调用 fsipc 去发送 IPC 请求以及解包并返回结果。fsipc 函数把发送请求到服务器和接收来自服务器的回复的普通细节做了简化处理。

fs/serv.c 中可以找到文件系统服务器代码。它是一个 serve 函数的循环,无休止地接收基于 IPC 的请求,并派发请求到相关的服务函数,并通过 IPC 来回送结果。在读取示例中,serve 将派发到 serve_read 函数上,它将去处理读取请求的 IPC 细节,比如,解包请求结构并最终调用 file_read 去执行实际的文件读取动作。

回顾一下 JOS 的 IPC 机制,它让一个环境发送一个单个的 32 位数字和可选的共享页。从一个客户端向服务器发送一个请求,我们为请求类型使用 32 位的数字(文件系统服务器 RPC 是有编号的,就像系统调用那样的编号),然后通过 IPC 在共享页上的一个 union Fsipc 中存储请求参数。在客户端侧,我们已经在 fsipcbuf 处共享了页;在服务端,我们在 fsreq0x0ffff000)处映射入站请求页。

服务器也通过 IPC 来发送响应。我们为函数的返回代码使用 32 位的数字。对于大多数 RPC,这已经涵盖了它们全部的返回代码。FSREQ_READFSREQ_STAT 也返回数据,它们只是被简单地写入到客户端发送它的请求时的页上。在 IPC 的响应中并不需要去发送这个页,因为这个页是文件系统服务器和客户端从一开始就共享的页。另外,在它的响应中,FSREQ_OPEN 与客户端共享一个新的 “Fd page”。我们将快捷地返回到文件描述符页上。

练习 5、实现 fs/serv.c 中的 serve_read

serve_read 的重任将由已经在 fs/fs.c 中实现的 file_read 来承担(它实际上不过是对 file_get_block 的一连串调用)。对于文件读取,serve_read 只能提供 RPC 接口。查看 serve_set_size 中的注释和代码,去大体上了解服务器函数的结构。

使用 make grade 去测试你的代码。你的代码通过 serveopen/filestat/file\_close 和 file\_read 的测试后,你得分应该是 70(总分为 150)。

.

练习 6、实现 fs/serv.c 中的 serve_writelib/file.c 中的 devfile_write

使用 make grade 去测试你的代码。你的代码通过 file\_write、fileread after filewrite、open、和 large file 的测试后,得分应该是 90(总分为150)。

进程增殖

我们给你提供了 spawn 的代码(查看 lib/spawn.c 文件),它用于创建一个新环境、从文件系统中加载一个程序镜像并启动子环境来运行这个程序。然后这个父进程独立于子环境持续运行。spawn 函数的行为,在效果上类似于UNIX 中的 fork,然后同时紧跟着 fork 之后在子进程中立即启动执行一个 exec

我们实现的是 spawn,而不是一个类 UNIX 的 exec,因为 spawn 是很容易从用户空间中、以”外内核式“ 实现的,它无需来自内核的特别帮助。为了在用户空间中实现 exec,想一想你应该做什么?确保你理解了它为什么很难。

练习 7spawn 依赖新的系统调用 sys_env_set_trapframe 去初始化新创建的环境的状态。实现 kern/syscall.c 中的 sys_env_set_trapframe。(不要忘记在 syscall() 中派发新系统调用)

运行来自 kern/init.c 中的 user/spawnhello 程序来测试你的代码kern/init.c ,它将尝试从文件系统中增殖 /hello

使用 make grade 去测试你的代码。

.

小挑战!实现 Unix 式的 exec

.

小挑战!实现 mmap 式的文件内存映射,并如果可能的话,修改 spawn 从 ELF 中直接映射页。

跨 fork 和 spawn 共享库状态

UNIX 文件描述符是一个通称的概念,它还包括管道、控制台 I/O 等等。在 JOS 中,每个这类设备都有一个相应的 struct Dev,使用指针去指向到实现读取/写入/等等的函数上。对于那个设备类型,lib/fd.c 在其上实现了类 UNIX 的文件描述符接口。每个 struct Fd 表示它的设备类型,并且大多数 lib/fd.c 中的函数只是简单地派发操作到 struct Dev 中相应函数上。

lib/fd.c 也在每个应用程序环境的地址空间中维护一个文件描述符表区域,开始位置在 FDTABLE 处。这个区域为应该程序能够一次最多打开 MAXFD(当前为 32)个文件描述符而保留页的地址空间值(4KB)。在任意给定的时刻,当且仅当相应的文件描述符处于使用中时,一个特定的文件描述符表才会被映射。在区域的 FILEDATA 处开始的位置,每个文件描述符表也有一个可选的”数据页“,如果它们被选定,相应的设备就能使用它。

我们想跨 forkspawn 共享文件描述符状态,但是文件描述符状态是保存在用户空间的内存中。而现在,在 fork 中,内存是标记为写时复制的,因此状态将被复制而不是共享。(这意味着环境不能在它们自己无法打开的文件中去搜索,并且管道不能跨一个 fork 去工作)在 spawn 上,内存将被保留,压根不会去复制。(事实上,增殖的环境从使用一个不打开的文件描述符去开始。)

我们将要修改 fork,以让它知道某些被”库管理的系统“所使用的、和总是被共享的内存区域。而不是去”硬编码“一个某些区域的列表,我们将在页表条目中设置一个”这些不使用“的位(就像我们在 fork 中使用的 PTE_COW 位一样)。

我们在 inc/lib.h 中定义了一个新的 PTE_SHARE 位,在 Intel 和 AMD 的手册中,这个位是被标记为”软件可使用的“的三个 PTE 位之一。我们将创建一个约定,如果一个页表条目中这个位被设置,那么在 forkspawn 中应该直接从父环境中复制 PTE 到子环境中。注意它与标记为写时复制的差别:正如在第一段中所描述的,我们希望确保能共享页更新。

练习 8、修改 lib/fork.c 中的 duppage,以遵循最新约定。如果页表条目设置了 PTE_SHARE 位,仅直接复制映射。(你应该去使用 PTE_SYSCALL,而不是 0xfff,去从页表条目中掩掉有关的位。0xfff 仅选出可访问的位和脏位。)

同样的,在 lib/spawn.c 中实现 copy_shared_pages。它应该循环遍历当前进程中所有的页表条目(就像 fork 那样),复制任何设置了 PTE_SHARE 位的页映射到子进程中。

使用 make run-testpteshare 去检查你的代码行为是否正确。正确的情况下,你应该会看到像 fork handles PTE_SHARE right 和 ”spawn handles PTE_SHARE right” 这样的输出行。

使用 make run-testfdsharing 去检查文件描述符是否正确共享。正确的情况下,你应该会看到 read in child succeeded 和 “read in parent succeeded” 这样的输出行。

键盘接口

为了能让 shell 工作,我们需要一些方式去输入。QEMU 可以显示输出,我们将它的输出写入到 CGA 显示器上和串行端口上,但到目前为止,我们仅能够在内核监视器中接收输入。在 QEMU 中,我们从图形化窗口中的输入作为从键盘到 JOS 的输入,虽然键入到控制台的输入作为出现在串行端口上的字符的方式显现。在 kern/console.c 中已经包含了由我们自实验 1 以来的内核监视器所使用的键盘和串行端口的驱动程序,但现在你需要去把这些增加到系统中。

练习 9、在你的 kern/trap.c 中,调用 kbd_intr 去处理捕获 IRQ_OFFSET+IRQ_KBDserial_intr,用它们去处理捕获 IRQ_OFFSET+IRQ_SERIAL

lib/console.c 中,我们为你实现了文件的控制台输入/输出。kbd_intrserial_intr 将使用从最新读取到的输入来填充缓冲区,而控制台文件类型去排空缓冲区(默认情况下,控制台文件类型为 stdin/stdout,除非用户重定向它们)。

运行 make run-testkbd 并输入几行来测试你的代码。在你输入完成之后,系统将回显你输入的行。如果控制台和窗口都可以使用的话,尝试在它们上面都做一下测试。

Shell

运行 make run-icodemake run-icode-nox 将运行你的内核并启动 user/icodeicode 又运行 init,它将设置控制台作为文件描述符 0 和 1(即:标准输入 stdin 和标准输出 stdout),然后增殖出环境 sh,就是 shell。之后你应该能够运行如下的命令了:

echo hello world | cat
cat lorem |cat
cat lorem |num
cat lorem |num |num |num |num |num
lsfd

注意用户库常规程序 cprintf 将直接输出到控制台,而不会使用文件描述符代码。这对调试非常有用,但是对管道连接其它程序却很不利。为将输出打印到特定的文件描述符(比如 1,它是标准输出 stdout),需要使用 fprintf(1, "...", ...)printf("...", ...) 是将输出打印到文件描述符 1(标准输出 stdout) 的快捷方式。查看 user/lsfd.c 了解更多示例。

练习 10、这个 shell 不支持 I/O 重定向。如果能够运行 run sh <script 就更完美了,就不用将所有的命令手动去放入一个脚本中,就像上面那样。为 <user/sh.c 中添加重定向的功能。

通过在你的 shell 中输入 sh <script 来测试你实现的重定向功能。

运行 make run-testshell 去测试你的 shell。testshell 只是简单地给 shell ”喂“上面的命令(也可以在 fs/testshell.sh 中找到),然后检查它的输出是否与 fs/testshell.key 一样。

.

小挑战!给你的 shell 添加更多的特性。最好包括以下的特性(其中一些可能会要求修改文件系统):

  • 后台命令 (ls &)
  • 一行中运行多个命令 (ls; echo hi)
  • 命令组 ((ls; echo hi) | cat > out)
  • 扩展环境变量 (echo $hello)
  • 引号 (echo "a | b")
  • 命令行历史和/或编辑功能
  • tab 命令补全
  • 为命令行查找目录、cd 和路径
  • 文件创建
  • 用快捷键 ctl-c 去杀死一个运行中的环境

可做的事情还有很多,并不仅限于以上列表。

到目前为止,你的代码应该要通过所有的测试。和以前一样,你可以使用 make grade 去评级你的提交,并且使用 make handin 上交你的实验。

本实验到此结束。 和以前一样,不要忘了运行 make grade 去做评级测试,并将你的练习答案和挑战问题的解决方案写下来。在动手实验之前,使用 git statusgit diff 去检查你的变更,并不要忘记使用 git add answers-lab5.txt 去提交你的答案。完成之后,使用 git commit -am 'my solutions to lab 5’ 去提交你的变更,然后使用 make handin 去提交你的解决方案。


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

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

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

这份指南将带你如何一步一步在 Linux 平台下利用 VirtualBox 安装 FreeDOS。

Linux 下借助 VirtualBox 安装 FreeDOS

2017 年的 11 月份,我采访了 Jim Hall 关于 FreeDOS 项目 背后的历史故事。今天,我将告诉你如何安装并使用 FreeDOS。需要注意到是:我将在 Solus(一种针对家庭用户的 Linux 桌面发行版)下使用 5.2.14 版本的 VirtualBox 来完成这些操作。

注意:在本教程我将使用 Solus 作为主机系统因为它很容易设置。另一个你需要注意的事情是 Solus 的软件中心有两个版本的 VirtualBox:virtualboxvirtualbox-current。Solus 会让你选择是使用 linux-lts 内核还是 linux-current 内核。最终区别就是,virtualbox 适用于 linux-lts 而 virtualbx-current 适用于 linux-current。

第一步 – 创建新的虚拟机

当你打开 VirtualBox,点击 “New” 按钮来新建一个虚拟机。你可以自定义这台虚拟机的名字,我将它命名为 “FreeDOS”。你也可以在标注栏内指明你正在安装的 FreeDOS 的版本。你还需要选择你将要安装的操作系统的类型和版本。选择 “Other” 下的 “DOS”。

第二步 – 设置内存大小

下一个对话框会问你要给 FreeDOS 主机分配多少可用的内存空间。默认分配 32 MB。不必更改它。在 DOS 系统盛行的年代,32 MB 大小的内存对于一台搭载 FreeDOS 的机器已经很足够了。如果你有需要,你可以通过对你针对 FreeDOS 新建的虚拟机右键并选择 “Setting -> Symtem” 来增加内存。

第三步 – 创建虚拟硬盘

下一步,你会被要求创建一个虚拟硬盘用来存储 FreeDOS 和它的文件。如果你还没有创建,只需要点击 “Create”。

下一个对话框会问你想用什么磁盘文件类型。默认的类型 (VirtualBox Disk Image) 效果就挺好。点击 “Next”。

下一个你遇到的问题是你想虚拟硬盘以何种方式创建。你是否希望虚拟硬盘占据的空间刚开始很小然后会随着你创建文件和安装软件逐渐增加直至达到你设置的上限?那么选择动态分配。如果你更喜欢虚拟硬盘 (VHD) 按照既定大小直接创建,选择固定大小即可。如果你不打算使用整个 VHD 或者你的硬盘空余空间不是太足够,那么动态分配是个很不错的分配方式。(需要注意的是,动态分配的虚拟硬盘占据的空间会随着你增加文件而增加,但不会因为你删除文件而变小) 我个人更喜欢动态分配,但你可以根据实际需要来选择最合适你的分配类型然后点击 “Next”。

现在,你可以选择虚拟磁盘的大小和位置。500 MB 已经很足够了。需要注意的是很多你之后用到的程序都是基于文本的,这意味着它们占据的空间非常小。在你做好这些调整后,点击 “Create”。

第四步 – 关联 .iso 文件

在我们继续之前,你需要下载 FreeDOS 的 .iso 文件。你需要选择 CDROM 格式的 “standard” 安装程序。

当文件下载完毕后,返回到 VirtualBox。选中你的虚拟机并打开设置。你可以通过对虚拟机右键并选中 “Setting” 或者选中虚拟机并点击 “Setting” 按钮。

接下来,点击 “Storage” 选项卡。在 “Storage Devices” 下面,选中 CD 图标。(它应该会在图标旁边显示 “Empty”。) 在右边的 “Attribute” 面板,点中 CD 图标然后在对应路径选中你刚下载的 .iso 文件。

提示:通常,在你通过 VirtualBox 安装完一个操作系统后你就可以删除对应的 .iso 文件了。但这并不适合 FreeDOS 。如果你想通过 FreeDOS 的包管理器来安装应用程序,你需要这个 .iso 文件。我通常会让这个 .iso 文件连接到虚拟机以便我安装一些程序。如果你也这么做了,你必须要确认下你让 FreeDOS 虚拟机每次启动的时候是从硬盘启动因为虚拟机的默认设置是从已关联的 .iso 文件启动。如果你忘了关联 .iso 文件,也不用担心。你可以通过选择 FreeDOS 虚拟机窗口上方的 “Devices” 来关联。然后就会发现 .iso 文件列在 “Optical Drives”。

第五步 – 安装 FreeDOS

既然我们已经完成了所有的准备工作,让我们来开始安装 FreeDOS 吧。

首先,你需要知道关于最新版本的 VirtualBox 的一个 bug。当我们创建好虚拟硬盘然后选中 “Install to harddisk” 后,如果你开启虚拟机你会发现在 FreeDOS 的欢迎界面出现过后就是不断滚动无群无尽的机器代码。我最近就遇到过这个问题而且不管是 Linux 还是 Windows 平台的 VirtualBox 都会碰到这个问题。(我知道解决办法。)

为了避开这个问题,你需要做一个简单的修改。当你看到 FreeDOS 的欢迎界面的时候,按下 Tab 键。(确认 “Install to harddrive” 已经选中。)在 “fdboot.img” 之后输入 raw 然后按下回车键。接下来就会启动 FreeDOS 的安装程序。

安装程序会首先处理你的虚拟磁盘的格式化。当格式化完成后,安装程序会重启。当 FreeDOS 的欢迎界面再次出现的时候,你必须重新输入 raw 就像你之前输入的内容那样。

要确保在安装过程中你遇到的所有问题你选的都是 “Yes”。但也要注意有一个很重要的问题:“What FreeDOS packages do you want to install?” 的答案并不是 “Yes” 或者 “No”。答案有两个选择分别是 “Base packages” 和 “Full installation”。“Base packages” 针对的是想体验类似原始的 MS-DOS 环境的人群。“Full installation” 则包括了一系列工具和实用的程序来提升 DOS。

在整个安装过程的最后,你可以选择重启或者继续停留在 DOS。选择“reboot”。

第六步 – 设置网络

不同于原始的 DOS,FreeDOS 可以访问互联网。你可以安装新的软件包或者更新你已经安装的软件包。要想使用网络,你还需要在 FreeDOS 安装些应用程序。

首先,启动进入你新创建的 FreeDOS 虚拟机。在 FreeDOS 的选择界面,选中 “Boot from System harddrive”。

现在,你可以通过输入 fdimples 来访问 FreeDOS 的软件包管理工具。你也可以借助方向键来浏览软件包管理器,然后用空格键选择类别或者软件包。在 “Networking” 类别中,你需要选中 fdnet。FreeDOS project 推荐也安装 mtcpwget。多次点击 Tab 键直到选中 “OK” 然后在按下回车键。安装完成后,输入 reboot 并按下回车键确认执行。系统重启后,引导你的系统驱动。如果网络安装成功的话,你会在终端看到一些关于你的网络信息的新消息。

注意:

有时候 VirtualBox 的默认设置并没有生效。如果遇到这种情况,先关闭你的 FreeDOS 虚拟机窗口。在 VirtualBox 主界面右键你的虚拟机并选中 “Setting”。VirtualBox 默认的网络设置是 “NAT”。将它改为 “Bridged Adapter” 后再尝试安装 FreeDOS 的软件包。现在就应该能正常运作了。

第七步 – FreeDOS 的基本使用

常见命令

既然你已经成功安装了 FreeDOS,让我们来看些基础命令。如果你已经在 Windows 平台使用过命令提示符,那么你会发现有很多命令都是相似的。

  • DIR– 显示当前目录的内容
  • CD – 改变当前所在的目录
  • COPY OLD.TXT NEW.TXT– 复制文件
  • TYPE TEST.TXT – 显示文件内容
  • DEL TEST.TXT – 删除文件
  • XCOPY DIR NEWDIR – 复制目录及目录下的所有内容
  • EDIT TEST.TXT– 编辑一个文件
  • MKDIR NEWDIR – 创建一个新目录
  • CLS – 清除屏幕

你可以借助互联网或者 Jim Hall 所创建的 方便的速查表 来找到更多基本的 DOS 命令。

运行一个程序

在 FreeDOS 上运行程序相当简单。需要注意的是当你借助 fdimples 软件包管理器来安装一个应用程序的时候,要确保你指定了待安装程序的 .EXE 文件的路径。这个路径会在应用程序的详细信息中显示。要想运行程序,通常你还需要进入到程序所在文件夹并输入该程序的名字。

例如,FreeDOS 中你可以安装一个叫 FED 的编辑器。安装完成后,你还需要做的就是进入 C:\FED 这个文件夹下并输入 FED

对于位于 \bin 这个文件夹的程序,比如 Pico。这些程序可以在任意文件夹中被调用。

对于游戏通常会有一个或者两个 .EXE 程序,你玩游戏之前不得不先运行它们。这些设置文件通常能够修复你遇到的声音,视频,或者控制问题。

如果你遇到一些本教程中没指出的问题,别忘记访问 FreeDOS 主站 来寻求解决办法。他们有一个 wiki 和一些其他的支持选项。

你使用过 FreeDOS 吗?你还想看关于 FreeDOS 哪些方面的教程?请在下面的评论区告诉我们。

如果你觉得本篇文章很有趣,请花一分钟的时间将它分享在你的社交媒体,Hacker News 或者 Reddit


via: https://itsfoss.com/install-freedos/

作者:John Paul 选题:lujun9972 译者:WangYueScream 校对:wxy

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

当前正是假日季,很多人可能已经在庆祝圣诞节了。祝你圣诞快乐,新年快乐。

为了延续节日氛围,我将向你展示一些非常棒的圣诞主题的 Linux 壁纸。在呈现这些壁纸之前,先来看一棵 Linux 终端下的圣诞树。

让你的桌面飘雪(针对 GNOME 用户)

如果您在 Ubuntu 18.04 或任何其他 Linux 发行版中使用 GNOME 桌面,您可以使用一个小的 GNOME 扩展并在桌面上飘雪。

您可以从软件中心或 GNOME 扩展网站获取此 gsnow 扩展。我建议您阅读一些关于使用 GNOME 扩展的内容。

安装此扩展程序后,您会在顶部面板上看到一个小雪花图标。 如果您单击一次,您会看到桌面屏幕上的小絮状物掉落。

你可以再次点击该图标来禁止雪花落下。

在 Linux 终端下显示圣诞树

如果你想要在终端里显示一个动画的圣诞树,你可以使用如下命令:

curl https://raw.githubusercontent.com/sergiolepore/ChristBASHTree/master/tree-EN.sh | bash

要是不想一直从互联网上获取这棵圣诞树,也可以从它的 GitHub 仓库 中获取对应的 shell 脚本,更改权限之后按照运行普通 shell 脚本的方式运行它。

使用 Perl 在 Linux 终端下显示圣诞树

Christmas Tree in Linux terminal by NixCraft

这个技巧最初由 NixCraft 分享,你需要为此安装 Perl 模块。

说实话,我不喜欢使用 Perl 模块,因为卸载它们真的很痛苦。所以使用这个 Perl 模块时需谨记,你必须手动移除它。

perl -MCPAN -e 'install Acme::POE::Tree'

你可以阅读 原文 来了解更多信息。

下载 Linux 圣诞主题壁纸

所有这些 Linux 圣诞主题壁纸都是由 Mark Riedesel 制作的,你可以在 他的网站 上找到很多其他艺术品。

自 2002 年以来,他几乎每年都在制作这样的壁纸。可以理解的是,最早的一些壁纸不具有现代的宽高比。我把它们按时间倒序排列。

注意一个小地方,这里显示的图片都是高度压缩的,因此你要通过图片下方提供的链接进行下载。

Christmas Linux Wallpaper

下载此壁纸

Christmas Linux Wallpaper

下载此壁纸

Christmas Linux Wallpapers

下载此壁纸

Christmas Linux Wallpapers

下载此壁纸

Christmas Linux Wallpapers

下载此壁纸

Christmas Linux Wallpapers

下载此壁纸

Christmas Linux Wallpapers

下载此壁纸

Christmas Linux Wallpapers

下载此壁纸

Christmas Linux Wallpapers

下载此壁纸

Christmas Linux Wallpapers

下载此壁纸

Christmas Linux Wallpapers

下载此壁纸

Christmas Linux Wallpapers

下载此壁纸

Christmas Linux Wallpapers

下载此壁纸

Christmas Linux Wallpapers

下载此壁纸

Christmas Linux Wallpapers

下载此壁纸

Christmas Linux Wallpapers

下载此壁纸

福利:Linux 圣诞颂歌

这是给你的一份福利,给像我们一样的 Linux 爱好者的关于 Linux 的圣诞颂歌。

《计算机世界》的一篇文章 中,Sandra Henry-Stocker 分享了这些圣诞颂歌。摘录片段如下:

这一段用的 Chestnuts Roasting on an Open Fire 的曲调:

Running merrily on open source

With users happy as can be

We’re using Linux and getting lots done

And happy everything is free

这一段用的 The Twelve Days of Christmas 的曲调:

On my first day with Linux, my admin gave to me a password and a login ID

On my second day with Linux my admin gave to me two new commands and a password and a login ID

这里 阅读完整的颂歌。

Linux 快乐!


via: https://itsfoss.com/christmas-linux-wallpaper/

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

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

如果你喜欢 Fedora、容器,而且有一块树莓派,那么这三者结合操控 LED 会怎么样?本文介绍的是 Fedora IoT,将展示如何在树莓派上安装预览镜像。还将学习如何与 GPIO 交互以点亮 LED。

什么是 Fedora IoT?

Fedora IoT 是当前 Fedora 项目的目标之一,计划成为一个完整的 Fedora 版本。Fedora IoT 将是一个在 ARM(目前仅限 aarch64)设备上(例如树莓派),以及 x86\_64 架构上运行的系统。

Fedora IoT 基于 OSTree 开发,就像 Fedora Silverblue 和以往的 Atomic Host

下载和安装 Fedora IoT

官方 Fedora IoT 镜像将和 Fedora 29 一起发布。但是在此期间你可以下载 基于 Fedora 28 的镜像 来进行这个实验。(LCTT 译注:截止至本译文发布,Fedora 29 已经发布了,但是 IoT 版本并未随同发布,或许会在 Fedora 30 一同发布?)

你有两种方法来安装这个系统:要么使用 dd 命令烧录 SD 卡,或者使用 fedora-arm-installer 工具。Fedora 的 Wiki 里面提供了为 IoT 设置物理设备 的更多信息。另外,你可能需要调整第三个分区的大小。

把 SD 卡插入到设备后,你需要创建一个用户来完成安装。这个步骤需要串行连接或一个 HDMI 显示器和键盘来与设备进行交互。

当系统安装完成后,下一步就是要设置网络连接。使用你刚才创建的用户登录系统,可以使用下列方式之一完成网络连接设置:

  • 如果你需要手动配置你的网络,可能需要执行类似如下命令,需要保证设置正确的网络地址:
$ nmcli connection add con-name cable ipv4.addresses \
192.168.0.10/24 ipv4.gateway 192.168.0.1 \
connection.autoconnect true ipv4.dns "8.8.8.8,1.1.1.1" \
type ethernet ifname eth0 ipv4.method manual
  • 如果你网络上运行着 DHCP 服务,可能需要类似如下命令:
$ nmcli con add type ethernet con-name cable ifname eth0

Fedora 中的 GPIO 接口

许多关于 Linux 上 GPIO 的教程都关注传统的 GPIO sysfis 接口。这个接口已经不推荐使用了,并且上游 Linux 内核社区由于安全和其他问题的缘故打算完全删除它。

Fedora 已经不将这个传统的接口编译到内核了,因此在系统上没有 /sys/class/gpio 这个文件。此教程使用一个上游内核提供的一个新的字符设备 /dev/gpiochipN 。这是目前和 GPIO 交互的方式。

为了和这个新设备进行交互,你需要使用一个库和一系列命令行界面的工具。常用的命令行工具比如说 echocat 在此设备上无法正常工作。

你可以通过安装 libgpiod-utils 包来安装命令行界面工具。python3-libgpiod 包提供了相应的 Python 库。

使用 Podman 来创建一个容器

Podman 是一个容器运行环境,其命令行界面类似于 Docker。Podman 的一大优势是它不会在后台运行任何守护进程。这对于资源有限的设备尤其有用。Podman 还允许您使用 systemd 单元文件启动容器化服务。此外,它还有许多其他功能。

我们使用如下两步来创建一个容器:

  1. 创建包含所需包的分层镜像。
  2. 使用分层镜像创建一个新容器。

首先创建一个 Dockerfile 文件,内容如下。这些内容告诉 Podman 基于可使用的最新 Fedora 镜像来构建我们的分层镜像。然后就是更新系统和安装一些软件包:

FROM fedora:latest
RUN  dnf -y update
RUN  dnf -y install libgpiod-utils python3-libgpiod

这样你就完成了镜像的生成前的配置工作,这个镜像基于最新的 Fedora,而且包含了和 GPIO 交互的软件包。

现在你就可以运行如下命令来构建你的基本镜像了:

$ sudo podman build --tag fedora:gpiobase -f ./Dockerfile

你已经成功创建了你的自定义镜像。这样以后你就可以不用每次都重新搭建环境了,而是基于你创建的镜像来完成工作。

使用 Podman 完成工作

为了确认当前的镜像是否就绪,可以运行如下命令:

$ sudo podman images
REPOSITORY                 TAG        IMAGE ID       CREATED          SIZE
localhost/fedora           gpiobase   67a2b2b93b4b   10 minutes ago  488MB
docker.io/library/fedora   latest     c18042d7fac6   2 days ago     300MB

现在,启动容器并进行一些实际的实验。容器通常是隔离的,无法访问主机系统,包括 GPIO 接口。因此需要在启动容器时将其挂载在容器内。可以使用以下命令中的 -device 选项来解决:

$ sudo podman run -it --name gpioexperiment --device=/dev/gpiochip0 localhost/fedora:gpiobase /bin/bash

运行之后就进入了正在运行的容器中。在继续之前,这里有一些容器命令。输入 exit 或者按下 Ctrl+D 来退出容器。

显示所有存在的容器可以运行如下命令,这包括当前没有运行的,比如你刚刚创建的那个:

$ sudo podman container ls -a
CONTAINER ID   IMAGE             COMMAND     CREATED          STATUS                              PORTS   NAMES
64e661d5d4e8   localhost/fedora:gpiobase   /bin/bash 37 seconds ago Exited (0) Less than a second ago           gpioexperiment

使用如下命令创建一个新的容器:

$ sudo podman run -it --name newexperiment --device=/dev/gpiochip0 localhost/fedora:gpiobase /bin/bash

如果想删除容器可以使用如下命令:

$ sudo podman rm newexperiment

点亮 LED 灯

现在可以使用已创建的容器。如果已经从容器退出,请使用以下命令再次启动它:

$ sudo podman start -ia gpioexperiment

如前所述,可以使用 Fedora 中 libgpiod-utils 包提供的命令行工具。要列出可用的 GPIO 芯片可以使用如下命令:

$ gpiodetect
gpiochip0 [pinctrl-bcm2835] (54 lines)

要获取特定芯片的连线列表,请运行:

$ gpioinfo gpiochip0

请注意,物理引脚数与前一个命令所打印的连线数之间没有相关性。重要的是 BCM 编号,如 pinout.xyz 所示。建议不要使用没有相应 BCM 编号的连线。

现在,将 LED 连接到物理引脚 40,也就是 BCM 21。请记住:LED 的短腿(负极,称为阴极)必须连接到带有 330 欧姆电阻的树莓派的 GND 引脚, 并且长腿(阳极)到物理引脚 40。

运行以下命令点亮 LED,按下 Ctrl + C 关闭:

$ gpioset --mode=wait gpiochip0 21=1

要点亮一段时间,请添加 -b(在后台运行)和 -s NUM(多少秒)参数,如下所示。 例如,要点亮 LED 5 秒钟,运行如下命令:

$ gpioset -b -s 5 --mode=time gpiochip0 21=1

另一个有用的命令是 gpioget。 它可以获得引脚的状态(高或低),可用于检测按钮和开关。

总结

你也可以使用 Python 操控 LED —— 这里有一些例子。 也可以在容器内使用 i2c 设备。 此外,Podman 与此 Fedora 版本并不严格相关。你可以在任何现有的 Fedora 版本上安装它,或者在 Fedora 中使用两个基于 OSTree 的新系统进行尝试:Fedora SilverblueFedora CoreOS


via: https://fedoramagazine.org/turnon-led-fedora-iot/

作者:Alessio Ciregia 选题:lujun9972 译者:ScarboroughCoral 校对:wxy

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

有些计算机网络需要在各个物理机器上维护相同的软件和配置。学校的计算机实验室就是这样的一个环境。 网络引导 服务器能够被配置为基于网络去提供一个完整的操作系统,以便于客户端计算机从一个中央位置获取配置。本教程将向你展示构建一台网络引导服务器的一种方法。

本教程的第一部分将包括创建一台网络引导服务器和镜像。第二部分将展示如何去添加 Kerberos 验证的家目录到网络引导配置中。

初始化配置

首先去下载 Fedora 服务器的 netinst 镜像,将它刻录到一张光盘上,然后用它引导服务器来重新格式化。我们只需要一个典型的 Fedora Server 的“最小化安装”来作为我们的开端,安装完成后,我们可以使用命令行去添加我们需要的任何额外的包。

注意:本教程中我们将使用 Fedora 28。其它版本在“最小化安装”中包含的包可能略有不同。如果你使用的是不同的 Fedora 版本,如果一个预期的文件或命令不可用,你可能需要做一些调试。

最小化安装的 Fedora Server 运行起来之后,以 root 用户登入:

$ sudo -i

并设置主机名字:

$ MY_HOSTNAME=server-01.example.edu
$ hostnamectl set-hostname $MY_HOSTNAME

注意:Red Hat 建议静态和临时名字应都要与这个机器在 DNS 中的完全合格域名相匹配,比如 host.example.com(了解主机名字)。

注意:本指南为了方便“复制粘贴”。需要自定义的任何值都声明为一个 MY_* 变量,在你运行剩余命令之前,你可能需要调整它。如果你注销之后,变量的赋值将被清除。

注意:Fedora 28 Server 在默认情况下往往会转储大量的日志到控制台上。你可以通过运行命令:sysctl -w kernel.printk=0 去禁用控制台日志输出。

接下来,我们需要在我们的服务器上配置一个静态网络地址。运行下面的一系列命令将找到并重新配置你的默认网络连接:

$ MY_DNS1=192.0.2.91
$ MY_DNS2=192.0.2.92
$ MY_IP=192.0.2.158
$ MY_PREFIX=24
$ MY_GATEWAY=192.0.2.254
$ DEFAULT_DEV=$(ip route show default | awk '{print $5}')
$ DEFAULT_CON=$(nmcli d show $DEFAULT_DEV | sed -n '/^GENERAL.CONNECTION:/s!.*:\s*!! p')
$ nohup bash << END
nmcli con mod "$DEFAULT_CON" connection.id "$DEFAULT_DEV"
nmcli con mod "$DEFAULT_DEV" connection.interface-name "$DEFAULT_DEV"
nmcli con mod "$DEFAULT_DEV" ipv4.method disabled
nmcli con up "$DEFAULT_DEV"
nmcli con add con-name br0 ifname br0 type bridge
nmcli con mod br0 bridge.stp no
nmcli con mod br0 ipv4.dns $MY_DNS1,$MY_DNS2
nmcli con mod br0 ipv4.addresses $MY_IP/$MY_PREFIX
nmcli con mod br0 ipv4.gateway $MY_GATEWAY
nmcli con mod br0 ipv4.method manual
nmcli con up br0
nmcli con add con-name br0-slave0 ifname "$DEFAULT_DEV" type bridge-slave master br0
nmcli con up br0-slave0
END

注意:上面最后的一组命令被封装到一个 nohup 脚本中,因为它将临时禁用网络。这个 nohup 命令可以让 nmcli 命令运行完成,即使你的 SSH 连接断开。注意,连接恢复可能需要 10 秒左右的时间,如果你改变了服务器 IP 地址,你将需要重新启动一个新的 SSH 连接。

注意:上面的网络配置在默认的连接之上创建了一个 网桥),这样我们在后面的测试中就可以直接运行一个虚拟机实例。如果你不想在这台服务器上去直接测试网络引导镜像,你可以跳过创建网桥的命令,并直接在你的默认网络连接上配置静态 IP 地址。

安装和配置 NFS4

从安装 nfs-utils 包开始:

$ dnf install -y nfs-utils

为发布 NFS 去创建一个顶级的 伪文件系统,然后在你的网络上共享它:

$ MY_SUBNET=192.0.2.0
$ mkdir /export
$ echo "/export -fsid=0,ro,sec=sys,root_squash $MY_SUBNET/$MY_PREFIX" > /etc/exports

SELinux 会干扰网络引导服务器的运行。为它配置例外规则超出了本教程中,因此我们这里直接禁用它:

$ sed -i '/GRUB_CMDLINE_LINUX/s/"$/ audit=0 selinux=0"/' /etc/default/grub
$ grub2-mkconfig -o /boot/grub2/grub.cfg
$ sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/sysconfig/selinux
$ setenforce 0
注意:应该不需要编辑 grub 命令行,但我们在测试过程中发现,直接编辑 /etc/sysconfig/selinux 被证明重启后是无效的,因此这样做再次确保设置了 selinux=0 标志。

现在,在本地防火墙中为 NFS 服务添加一个例外规则,然后启动 NFS 服务:

$ firewall-cmd --add-service nfs
$ firewall-cmd --runtime-to-permanent
$ systemctl enable nfs-server.service
$ systemctl start nfs-server.service

创建网络引导镜像

现在我们的 NFS 服务器已经启动运行了,我们需要为它提供一个操作系统镜像,以便于它提供给客户端计算机。我们将从一个非常小的镜像开始,等一切顺利之后再添加。

首先,创建一个存放我们镜像的新目录:

$ mkdir /fc28

使用 dnf 命令在新目录下用几个基础包去构建镜像:

$ dnf -y --releasever=28 --installroot=/fc28 install fedora-release systemd passwd rootfiles sudo dracut dracut-network nfs-utils vim-minimal dnf

在上面的命令中省略了很重要的 kernel 包。在它们被安装完成之前,我们需要去调整一下 initramfs 镜像中包含的驱动程序集,kernel 首次安装时将自动构建这个镜像。尤其是,我们需要禁用 hostonly 模式,以便于 initramfs 镜像能够在各种硬件平台上正常工作,并且我们还需要添加对网络和 NFS 的支持:

$ echo 'hostonly=no' > /fc28/etc/dracut.conf.d/hostonly.conf
$ echo 'add_dracutmodules+=" network nfs "' > /fc28/etc/dracut.conf.d/netboot.conf

现在,安装 kernel 包:

$ dnf -y --installroot=/fc28 install kernel

设置一个阻止 kernel 包被更新的规则:

$ echo 'exclude=kernel-*' >> /fc28/etc/dnf/dnf.conf

设置 locale:

$ echo 'LANG="en_US.UTF-8"' > /fc28/etc/locale.conf
注意:如果 locale 没有正确配置,一些程序(如 GNOME Terminal)将无法正常工作。

设置客户端的主机名字:

$ MY_CLIENT_HOSTNAME=client-01.example.edu
$ echo $MY_CLIENT_HOSTNAME > /fc28/etc/hostname

禁用控制台日志输出:

$ echo 'kernel.printk = 0 4 1 7' > /fc28/etc/sysctl.d/00-printk.conf

定义网络引导镜像中的本地 liveuser 用户:

$ echo 'liveuser:x:1000:1000::/home/liveuser:/bin/bash' >> /fc28/etc/passwd
$ echo 'liveuser::::::::' >> /fc28/etc/shadow
$ echo 'liveuser:x:1000:' >> /fc28/etc/group
$ echo 'liveuser:!::' >> /fc28/etc/gshadow

允许 liveuser 使用 sudo

$ echo 'liveuser ALL=(ALL) NOPASSWD: ALL' > /fc28/etc/sudoers.d/liveuser

启用自动创建家目录:

$ dnf install -y --installroot=/fc28 authselect oddjob-mkhomedir
$ echo 'dirs /home' > /fc28/etc/rwtab.d/home
$ chroot /fc28 authselect select sssd with-mkhomedir --force
$ chroot /fc28 systemctl enable oddjobd.service

由于多个客户端将会同时挂载我们的镜像,我们需要去配置镜像工作在只读模式中:

$ sed -i 's/^READONLY=no$/READONLY=yes/' /fc28/etc/sysconfig/readonly-root

配置日志输出到内存而不是持久存储中:

$ sed -i 's/^#Storage=auto$/Storage=volatile/' /fc28/etc/systemd/journald.conf

配置 DNS:

$ MY_DNS1=192.0.2.91
$ MY_DNS2=192.0.2.92
$ cat << END > /fc28/etc/resolv.conf
nameserver $MY_DNS1
nameserver $MY_DNS2
END

绕开编写本教程时存在的根目录只读挂载的 bug(BZ1542567):

$ echo 'dirs /var/lib/gssproxy' > /fc28/etc/rwtab.d/gssproxy
$ cat << END > /fc28/etc/rwtab.d/systemd
dirs /var/lib/systemd/catalog
dirs /var/lib/systemd/coredump
END

最后,为我们镜像创建 NFS 文件系统,并将它共享到我们的子网中:

$ mkdir /export/fc28
$ echo '/fc28 /export/fc28 none bind 0 0' >> /etc/fstab
$ mount /export/fc28
$ echo "/export/fc28 -ro,sec=sys,no_root_squash $MY_SUBNET/$MY_PREFIX" > /etc/exports.d/fc28.exports
$ exportfs -vr

创建引导加载器

现在,我们已经有了可以进行网络引导的操作系统,我们需要一个引导加载器去从客户端系统上启动它。在本教程中我们使用的是 iPXE

注意:本节和接下来的节使用 QEMU 测试,也能在另外一台单独的计算机上来完成;它们并不需要在网络引导服务器上来运行。

安装 git 并使用它去下载 iPXE:

$ dnf install -y git
$ git clone http://git.ipxe.org/ipxe.git $HOME/ipxe

现在我们需要去为我们的引导加载器创建一个指定的启动脚本:

$ cat << 'END' > $HOME/ipxe/init.ipxe
#!ipxe

prompt --key 0x02 --timeout 2000 Press Ctrl-B for the iPXE command line... && shell ||

dhcp || exit
set prefix file:///linux
chain ${prefix}/boot.cfg || exit
END

启动 “file” 下载协议:

$ echo '#define DOWNLOAD_PROTO_FILE' > $HOME/ipxe/src/config/local/general.h

安装 C 编译器以及相关的工具和库:

$ dnf groupinstall -y "C Development Tools and Libraries"

构建引导加载器:

$ cd $HOME/ipxe/src
$ make clean
$ make bin-x86_64-efi/ipxe.efi EMBED=../init.ipxe

记下新编译的引导加载器的存储位置。我们将在接下来的节中用到它:

$ IPXE_FILE="$HOME/ipxe/src/bin-x86_64-efi/ipxe.efi"

用 QEMU 测试

这一节是可选的,但是你需要去复制下面显示在物理机器上的 EFI 系统分区 的布局,在网络引导时需要去配置它们。

注意:如果你想实现一个完全的无盘系统,你也可以复制那个文件到一个 TFTP 服务器,然后从 DHCP 上指向那台服务器。

为了使用 QEMU 去测试我们的引导加载器,我们继续去创建一个仅包含一个 EFI 系统分区和我们的启动文件的、很小的磁盘镜像。

从创建 EFI 系统分区所需要的目录布局开始,然后把我们在前面节中创建的引导加载器复制进去:

$ mkdir -p $HOME/esp/efi/boot
$ mkdir $HOME/esp/linux
$ cp $IPXE_FILE $HOME/esp/efi/boot/bootx64.efi

下面的命令将识别我们的引导加载器镜像正在使用的内核版本,并将它保存到一个变量中,以备后续的配置命令去使用它:

$ DEFAULT_VER=$(ls -c /fc28/lib/modules | head -n 1)

定义我们的客户端计算机将使用的引导配置:

$ MY_DNS1=192.0.2.91
$ MY_DNS2=192.0.2.92
$ MY_NFS4=server-01.example.edu
$ cat << END > $HOME/esp/linux/boot.cfg
#!ipxe

kernel --name kernel.efi \${prefix}/vmlinuz-$DEFAULT_VER initrd=initrd.img ro ip=dhcp rd.peerdns=0 nameserver=$MY_DNS1 nameserver=$MY_DNS2 root=nfs4:$MY_NFS4:/fc28 console=tty0 console=ttyS0,115200n8 audit=0 selinux=0 quiet
initrd --name initrd.img \${prefix}/initramfs-$DEFAULT_VER.img
boot || exit
END
注意:上面的引导脚本展示了如何使用 iPXE 去网络引导 Linux 的最小示例。还可以做更多更复杂的配置。值得注意的是,iPXE 支持交互式引导菜单,它可以让你配置默认选项和超时时间。比如,一个更高级一点 iPXE 脚本可以默认从本地磁盘引导一个操作系统,如果在倒计时结束之前用户按下了一个键,才会去网络引导一个操作系统。

复制 Linux 内核并分配 initramfs 给 EFI 系统分区:

$ cp $(find /fc28/lib/modules -maxdepth 2 -name 'vmlinuz' | grep -m 1 $DEFAULT_VER) $HOME/esp/linux/vmlinuz-$DEFAULT_VER
$ cp $(find /fc28/boot -name 'init*' | grep -m 1 $DEFAULT_VER) $HOME/esp/linux/initramfs-$DEFAULT_VER.img

我们最终的目录布局应该看起来像下面的样子:

esp
├── efi
│   └── boot
│   └── bootx64.efi
└── linux
 ├── boot.cfg
 ├── initramfs-4.18.18-200.fc28.x86_64.img
 └── vmlinuz-4.18.18-200.fc28.x86_64

要让 QEMU 去使用我们的 EFI 系统分区,我们需要去创建一个小的 uefi.img 磁盘镜像来包含它,然后将它连接到 QEMU 作为主引导驱动器。

开始安装必需的工具:

$ dnf install -y parted dosfstools

现在创建 uefi.img 文件,并将 esp 目录中的文件复制进去:

$ ESP_SIZE=$(du -ks $HOME/esp | cut -f 1)
$ dd if=/dev/zero of=$HOME/uefi.img count=$((${ESP_SIZE}+5000)) bs=1KiB
$ UEFI_DEV=$(losetup --show -f $HOME/uefi.img)
$ parted ${UEFI_DEV} -s mklabel gpt mkpart EFI FAT16 1MiB 100% toggle 1 boot
$ mkfs -t msdos ${UEFI_DEV}p1
$ mkdir -p $HOME/mnt
$ mount ${UEFI_DEV}p1 $HOME/mnt
$ cp -r $HOME/esp/* $HOME/mnt
$ umount $HOME/mnt
$ losetup -d ${UEFI_DEV}

注意:在物理计算机上,你只需要从 esp 目录中复制文件到计算机上已存在的 EFI 系统分区中。你不需要使用 uefi.img 文件去引导物理计算机。

注意:在一个物理计算机上,如果文件名已存在,你可以重命名 bootx64.efi 文件,如果你重命名了它,就需要去编辑计算机的 BIOS 设置,并添加重命令后的 efi 文件到引导列表中。

接下来我们需要去安装 qemu 包:

$ dnf install -y qemu-system-x86

允许 QEMU 访问我们在本教程“初始化配置”一节中创建的网桥:

$ echo 'allow br0' > /etc/qemu/bridge.conf

创建一个 OVMF_VARS.fd 镜像的副本去保存我们虚拟机的持久 BIOS 配置:

$ cp /usr/share/edk2/ovmf/OVMF_VARS.fd $HOME

现在,启动虚拟机:

$ qemu-system-x86_64 -machine accel=kvm -nographic -m 1024 -drive if=pflash,format=raw,unit=0,file=/usr/share/edk2/ovmf/OVMF_CODE.fd,readonly=on -drive if=pflash,format=raw,unit=1,file=$HOME/OVMF_VARS.fd -drive if=ide,format=raw,file=$HOME/uefi.img -net bridge,br=br0 -net nic,model=virtio

如果一切顺利,你将看到类似下图所示的结果:

你可以使用 shutdown 命令关闭虚拟机回到我们的服务器上:

$ sudo shutdown -h now
注意:如果出现了错误或虚拟机挂住了,你可能需要启动一个新的 SSH 会话去连接服务器,使用 kill 命令去终止 qemu-system-x86_64 进程。

镜像中添加包

镜像中添加包应该是一个很简单的问题,在服务器上 chroot 进镜像,然后运行 dnf install <package_name>

在网络引导镜像中并不限制你能安装什么包。一个完整的图形化安装应该能够完美地工作。

下面是一个如何将最小化安装的网络引导镜像变成完整的图形化安装的示例:

$ for i in dev dev/pts dev/shm proc sys run; do mount -o bind /$i /fc28/$i; done
$ chroot /fc28 /usr/bin/bash --login
$ dnf -y groupinstall "Fedora Workstation"
$ dnf -y remove gnome-initial-setup
$ systemctl disable sshd.service
$ systemctl enable gdm.service
$ systemctl set-default graphical.target
$ sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/sysconfig/selinux
$ logout
$ for i in run sys proc dev/shm dev/pts dev; do umount /fc28/$i; done

可选地,你可能希望去启用 liveuser 用户的自动登录:

$ sed -i '/daemon/a AutomaticLoginEnable=true' /fc28/etc/gdm/custom.conf
$ sed -i '/daemon/a AutomaticLogin=liveuser' /fc28/etc/gdm/custom.conf

via: https://fedoramagazine.org/how-to-build-a-netboot-server-part-1/

作者:Gregory Bartholomew 选题:lujun9972 译者:qhwdw 校对:wxy

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