Merge remote-tracking branch 'LCTT/master'

This commit is contained in:
Xingyu.Wang 2018-12-16 19:09:27 +08:00
commit 66c0816519
7 changed files with 328 additions and 362 deletions

View File

@ -1,10 +1,9 @@
实验 4抢占式多任务处理
Caffeinated 6.828实验 4抢占式多任务处理
======
### 实验 4抢占式多任务处理
#### 简介
### 简介
在本实验中,你将在多个同时活动的用户模式中的环境之间实现抢占式多任务处理。
在本实验中,你将在多个同时活动的用户模式环境之间实现抢占式多任务处理。
在 Part A 中,你将在 JOS 中添加对多处理器的支持,以实现循环调度。并且添加基本的环境管理方面的系统调用(创建和销毁环境的系统调用、以及分配/映射内存)。
@ -12,7 +11,7 @@
最后,在 Part C 中,你将在 JOS 中添加对进程间通讯IPC的支持以允许不同用户模式环境之间进行显式通讯和同步。你也将要去添加对硬件时钟中断和优先权的支持。
##### 预备知识
#### 预备知识
使用 git 去提交你的实验 3 的源代码,并获取课程仓库的最新版本,然后创建一个名为 `lab4` 的本地分支,它跟踪我们的名为 `origin/lab4` 的远程 `lab4` 分支:
@ -31,6 +30,7 @@
```
实验 4 包含了一些新的源文件,在开始之前你应该去浏览一遍:
```markdown
kern/cpu.h Kernel-private definitions for multiprocessor support
kern/mpconfig.c Code to read the multiprocessor configuration
@ -41,19 +41,19 @@ kern/spinlock.c Kernel code implementing spin locks
kern/sched.c Code skeleton of the scheduler that you are about to implement
```
##### 实验要求
#### 实验要求
本实验分为三部分Part A、Part B和 Part C。我们计划为每个部分分配一周的时间。
本实验分为三部分Part A、Part B 和 Part C。我们计划为每个部分分配一周的时间。
和以前一样,你需要完成实验中出现的、所有常规练习和至少一个挑战问题。(不是每个部分做一个挑战问题,是整个实验做一个挑战问题即可。)另外,你还要写出你实现的挑战问题的详细描述。如果你实现了多个挑战问题,你只需写出其中一个即可,虽然我们的课程欢迎你完成越多的挑战越好。在动手实验之前,请将你的挑战问题的答案写在一个名为 `answers-lab4.txt` 的文件中,并把它放在你的 `lab` 目录的根下。
#### Part A多处理器支持和协调多任务处理
### Part A多处理器支持和协调多任务处理
在本实验的第一部分,将去扩展你的 JOS 内核,以便于它能够在一个多处理器的系统上运行,并且要在 JOS 内核中实现一些新的系统调用,以便于它允许用户级环境创建附加的新环境。你也要去实现协调的循环调度,在当前的环境自愿放弃 CPU或退出允许内核将一个环境切换到另一个环境。稍后在 Part C 中,你将要实现抢占调度,它允许内核在环境占有 CPU 一段时间后,从这个环境上重新取回对 CPU 的控制,那怕是在那个环境不配合的情况下。
##### 多处理器支持
#### 多处理器支持
我们继续去让 JOS 支持 “对称多处理器”SMP在一个多处理器的模型中所有 CPU 们都有平等访问系统资源(如内存和 I/O 总线)的权。虽然在 SMP 中所有 CPU 们都有相同的功能但是在引导进程的过程中它们被分成两种类型引导程序处理器BSP负责初始化系统和引导操作系统而在操作系统启动并正常运行后应用程序处理器AP将被 BSP 激活。哪个处理器做 BSP 是由硬件和 BIOS 来决定的。到目前为止,你所有的已存在的 JOS 代码都是运行在 BSP 上的。
我们继续去让 JOS 支持 “对称多处理器”SMP在一个多处理器的模型中所有 CPU 们都有平等访问系统资源(如内存和 I/O 总线)的权。虽然在 SMP 中所有 CPU 们都有相同的功能但是在引导进程的过程中它们被分成两种类型引导程序处理器BSP负责初始化系统和引导操作系统而在操作系统启动并正常运行后应用程序处理器AP将被 BSP 激活。哪个处理器做 BSP 是由硬件和 BIOS 来决定的。到目前为止,你所有的已存在的 JOS 代码都是运行在 BSP 上的。
在一个 SMP 系统上,每个 CPU 都伴有一个本地 APICLAPIC单元。这个 LAPIC 单元负责传递系统中的中断。LAPIC 还为它所连接的 CPU 提供一个唯一的标识符。在本实验中,我们将使用 LAPIC 单元(它在 `kern/lapic.c` 中)中的下列基本功能:
@ -61,15 +61,11 @@ kern/sched.c Code skeleton of the scheduler that you are about to implement
* 从 BSP 到 AP 之间发送处理器间中断IPI `STARTUP`,以启动其它 CPU查看 `lapic_startap()`)。
* 在 Part C 中,我们设置 LAPIC 的内置定时器去触发时钟中断,以便于支持抢占式多任务处理(查看 `apic_init()`)。
一个处理器使用内存映射的 I/OMMIO来访问它的 LAPIC。在 MMIO 中,一部分物理内存是硬编码到一些 I/O 设备的寄存器中,因此,访问内存时一般可以使用相同的 `load/store` 指令去访问设备的寄存器。正如你所看到的,在物理地址 `0xA0000` 处就是一个 IO 入口(就是我们写入 VGA 缓冲区的入口。LAPIC 就在那里,它从物理地址 `0xFE000000`4GB 减去 32MB 处)开始,这个地址对于我们在 KERNBASE 处使用直接映射访问来说太高了。JOS 虚拟内存映射在 `MMIOBASE` 处,留下一个 4MB 的空隙,以便于我们有一个地方,能像这样去映射设备。由于在后面的实验中,我们将介绍更多的 MMIO 区域,你将要写一个简单的函数,从这个区域中去分配空间,并将设备的内存映射到那里。
> **练习 1**、实现 `kern/pmap.c` 中的 `mmio_map_region`。去看一下它是如何使用的,从 `kern/lapic.c` 中的 `lapic_init` 开始看起。在 `mmio_map_region` 的测试运行之前,你还要做下一个练习。
一个处理器使用内存映射的 I/OMMIO来访问它的 LAPIC。在 MMIO 中,一部分物理内存是硬编码到一些 I/O 设备的寄存器中,因此,访问内存时一般可以使用相同的 `load/store` 指令去访问设备的寄存器。正如你所看到的,在物理地址 `0xA0000` 处就是一个 IO 入口(就是我们写入 VGA 缓冲区的入口。LAPIC 就在那里,它从物理地址 `0xFE000000`4GB 减去 32MB 处)开始,这个地址对于我们在 KERNBASE 处使用直接映射访问来说太高了。JOS 虚拟内存映射在 `MMIOBASE` 处,留下一个 4MB 的空隙,以便于我们有一个地方,能像这样去映射设备。由于在后面的实验中,我们将介绍更多的 MMIO 区域,你将要写一个简单的函数,从这个区域中去分配空间,并将设备的内存映射到那里。
```markdown
练习 1、实现 `kern/pmap.c` 中的 `mmio_map_region`。去看一下它是如何使用的,从 `kern/lapic.c` 中的 `lapic_init` 开始看起。在 `mmio_map_region` 的测试运行之前,你还要做下一个练习。
```
###### 引导应用程序处理器
##### 引导应用程序处理器
在引导应用程序处理器之前,引导程序处理器应该会首先去收集关于多处理器系统的信息,比如总的 CPU 数、它们的 APIC ID 以及 LAPIC 单元的 MMIO 地址。在 `kern/mpconfig.c` 中的 `mp_init()` 函数,通过读取内存中位于 BIOS 区域里的 MP 配置表来获得这些信息。
@ -77,46 +73,42 @@ kern/sched.c Code skeleton of the scheduler that you are about to implement
在那之后,通过发送 IPI `STARTUP` 到相关 AP 的 LAPIC 单元,以及一个初始的 `CS:IP` 地址AP 将从那儿开始运行它的入口代码,在我们的案例中是 `MPENTRY_PADDR` `boot_aps()` 将一个接一个地激活 AP。在 `kern/mpentry.S` 中的入口代码非常类似于 `boot/boot.S`。在一些简短的设置之后,它启用分页,使 AP 进入保护模式,然后调用 C 设置程序 `mp_main()`(它也在 `kern/init.c` 中)。在继续唤醒下一个 AP 之前, `boot_aps()` 将等待这个 AP 去传递一个 `CPU_STARTED` 标志到它的 `struct CpuInfo` 中的 `cpu_status` 字段中。
```markdown
练习 2、阅读 `kern/init.c` 中的 `boot_aps()``mp_main()`,以及在 `kern/mpentry.S` 中的汇编代码。确保你理解了在 AP 引导过程中的控制流转移。然后修改在 `kern/pmap.c` 中的、你自己的 `page_init()`,实现避免在 `MPENTRY_PADDR` 处添加页到空闲列表上,以便于我们能够在物理地址上安全地复制和运行 AP 引导程序代码。你的代码应该会通过更新后的 `check_page_free_list()` 的测试(但可能会在更新后的 `check_kern_pgdir()` 上测试失败,我们在后面会修复它)。
```
> **练习 2**、阅读 `kern/init.c` 中的 `boot_aps()``mp_main()`,以及在 `kern/mpentry.S` 中的汇编代码。确保你理解了在 AP 引导过程中的控制流转移。然后修改在 `kern/pmap.c` 中的、你自己的 `page_init()`,实现避免在 `MPENTRY_PADDR` 处添加页到空闲列表上,以便于我们能够在物理地址上安全地复制和运行 AP 引导程序代码。你的代码应该会通过更新后的 `check_page_free_list()` 的测试(但可能会在更新后的 `check_kern_pgdir()` 上测试失败,我们在后面会修复它)。
```markdown
问题
1、比较 `kern/mpentry.S``boot/boot.S`。记住,那个 `kern/mpentry.S` 是编译和链接后的,运行在 `KERNBASE` 上面的,就像内核中的其它程序一样,宏 `MPBOOTPHYS` 的作用是什么?为什么它需要在 `kern/mpentry.S` 中,而不是在 `boot/boot.S` 中?换句话说,如果在 `kern/mpentry.S` 中删掉它,会发生什么错误?
.
> **问题 1**、比较 `kern/mpentry.S``boot/boot.S`。记住,那个 `kern/mpentry.S` 是编译和链接后的,运行在 `KERNBASE` 上面的,就像内核中的其它程序一样,宏 `MPBOOTPHYS` 的作用是什么?为什么它需要在 `kern/mpentry.S` 中,而不是在 `boot/boot.S` 中?换句话说,如果在 `kern/mpentry.S` 中删掉它,会发生什么错误?
提示:回顾链接地址和加载地址的区别,我们在实验 1 中讨论过它们。
```
###### 每个 CPU 的状态和初始化
##### 每个 CPU 的状态和初始化
当写一个多处理器操作系统时,区分每个 CPU 的状态是非常重要的,而每个 CPU 的状态对其它处理器是不公开的,而全局状态是整个系统共享的。`kern/cpu.h` 定义了大部分每个 CPU 的状态,包括 `struct CpuInfo`,它保存了每个 CPU 的变量。`cpunum()` 总是返回调用它的那个 CPU 的 ID它可以被用作是数组的索引比如 `cpus`。或者,宏 `thiscpu` 是当前 CPU 的 `struct CpuInfo` 缩略表示。
下面是你应该知道的每个 CPU 的状态:
* **每个 CPU 的内核栈**
因为内核能够同时捕获多个 CPU因此我们需要为每个 CPU 准备一个单独的内核栈,以防止它们运行的程序之间产生相互干扰。数组 `percpu_kstacks[NCPU][KSTKSIZE]` 为 NCPU 的内核栈资产保留了空间。
* **每个 CPU 的内核栈**
在实验 2 中,你映射的 `bootstack` 所引用的物理内存,就作为 `KSTACKTOP` 以下的 BSP 的内核栈。同样,在本实验中,你将每个 CPU 的内核栈映射到这个区域而使用保护页做为它们之间的缓冲区。CPU 0 的栈将从 `KSTACKTOP` 处向下增长CPU 1 的栈将从 CPU 0 的栈底部的 `KSTKGAP` 字节处开始,依次类推。在 `inc/memlayout.h` 中展示了这个映射布局
因为内核能够同时捕获多个 CPU因此我们需要为每个 CPU 准备一个单独的内核栈,以防止它们运行的程序之间产生相互干扰。数组 `percpu_kstacks[NCPU][KSTKSIZE]` 为 NCPU 的内核栈资产保留了空间
* **每个 CPU 的 TSS 和 TSS 描述符**
为了指定每个 CPU 的内核栈在哪里,也需要有一个每个 CPU 的任务状态描述符TSS。CPU _i_ 的任务状态描述符是保存在 `cpus[i].cpu_ts` 中,而对应的 TSS 描述符是定义在 GDT 条目 `gdt[(GD_TSS0 >> 3) + i]` 中。在 `kern/trap.c` 中定义的全局变量 `ts` 将不再被使用。
在实验 2 中,你映射的 `bootstack` 所引用的物理内存,就作为 `KSTACKTOP` 以下的 BSP 的内核栈。同样,在本实验中,你将每个 CPU 的内核栈映射到这个区域而使用保护页做为它们之间的缓冲区。CPU 0 的栈将从 `KSTACKTOP` 处向下增长CPU 1 的栈将从 CPU 0 的栈底部的 `KSTKGAP` 字节处开始,依次类推。在 `inc/memlayout.h` 中展示了这个映射布局。
* **每个 CPU 当前的环境指针**
由于每个 CPU 都能同时运行不同的用户进程,所以我们重新定义了符号 `curenv`,让它指向到 `cpus[cpunum()].cpu_env`(或 `thiscpu->cpu_env`),它指向到当前 CPU代码正在运行的那个 CPU上当前正在运行的环境上。
* **每个 CPU 的 TSS 和 TSS 描述符**
* **每个 CPU 的系统寄存器**
所有的寄存器,包括系统寄存器,都是一个 CPU 私有的。所以,初始化这些寄存器的指令,比如 `lcr3()`、`ltr()`、`lgdt()`、`lidt()`、等待,必须在每个 CPU 上运行一次。函数 `env_init_percpu()``trap_init_percpu()` 就是为此目的而定义的。
为了指定每个 CPU 的内核栈在哪里,也需要有一个每个 CPU 的任务状态描述符TSS。CPU _i_ 的任务状态描述符是保存在 `cpus[i].cpu_ts` 中,而对应的 TSS 描述符是定义在 GDT 条目 `gdt[(GD_TSS0 >> 3) + i]` 中。在 `kern/trap.c` 中定义的全局变量 `ts` 将不再被使用。
* **每个 CPU 当前的环境指针**
由于每个 CPU 都能同时运行不同的用户进程,所以我们重新定义了符号 `curenv`,让它指向到 `cpus[cpunum()].cpu_env`(或 `thiscpu->cpu_env`),它指向到当前 CPU代码正在运行的那个 CPU上当前正在运行的环境上。
```markdown
练习 3、修改 `mem_init_mp()`(在 `kern/pmap.c` 中)去映射每个 CPU 的栈从 `KSTACKTOP` 处开始,就像在 `inc/memlayout.h` 中展示的那样。每个栈的大小是 `KSTKSIZE` 字节加上未映射的保护页 `KSTKGAP` 的字节。你的代码应该会通过在 `check_kern_pgdir()` 中的新的检查。
```
* **每个 CPU 的系统寄存器**
```markdown
练习 4、在 `trap_init_percpu()`(在 `kern/trap.c` 文件中)的代码为 BSP 初始化 TSS 和 TSS 描述符。在实验 3 中它就运行过,但是当它运行在其它的 CPU 上就会出错。修改这些代码以便它能在所有 CPU 上都正常运行。(注意:你的新代码应该还不能使用全局变量 `ts`
```
所有的寄存器,包括系统寄存器,都是一个 CPU 私有的。所以,初始化这些寄存器的指令,比如 `lcr3()`、`ltr()`、`lgdt()`、`lidt()`、等待,必须在每个 CPU 上运行一次。函数 `env_init_percpu()``trap_init_percpu()` 就是为此目的而定义的。
> **练习 3**、修改 `mem_init_mp()`(在 `kern/pmap.c` 中)去映射每个 CPU 的栈从 `KSTACKTOP` 处开始,就像在 `inc/memlayout.h` 中展示的那样。每个栈的大小是 `KSTKSIZE` 字节加上未映射的保护页 `KSTKGAP` 的字节。你的代码应该会通过在 `check_kern_pgdir()` 中的新的检查。
.
> **练习 4**、在 `trap_init_percpu()`(在 `kern/trap.c` 文件中)的代码为 BSP 初始化 TSS 和 TSS 描述符。在实验 3 中它就运行过,但是当它运行在其它的 CPU 上就会出错。修改这些代码以便它能在所有 CPU 上都正常运行。(注意:你的新代码应该还不能使用全局变量 `ts`
在你完成上述练习后,在 QEMU 中使用 4 个 CPU使用 `make qemu CPUS=4``make qemu-nox CPUS=4`)来运行 JOS你应该看到类似下面的输出
@ -134,94 +126,87 @@ kern/sched.c Code skeleton of the scheduler that you are about to implement
SMP: CPU 3 starting
```
###### 锁定
##### 锁定
`mp_main()` 中初始化 AP 后我们的代码快速运行起来。在你更进一步增强 AP 之前,我们需要首先去处理多个 CPU 同时运行内核代码的争用状况。达到这一目标的最简单的方法是使用大内核锁。大内核锁是一个单个的全局锁,当一个环境进入内核模式时,它将被加锁,而这个环境返回到用户模式时它将释放锁。在这种模型中,在用户模式中运行的环境可以同时运行在任何可用的 CPU 上,但是只有一个环境能够运行在内核模式中;而任何尝试进入内核模式的其它环境都被强制等待。
`kern/spinlock.h` 中声明大内核锁,即 `kernel_lock`。它也提供 `lock_kernel()``unlock_kernel()`,快捷地去获取/释放锁。你应该在以下的四个位置应用大内核锁:
* 在 `i386_init()` 时,在 BSP 唤醒其它 CPU 之前获取锁。
* 在 `mp_main()` 时,在初始化 AP 之后获取锁,然后调用 `sched_yield()` 在这个 AP 上开始运行环境。
* 在 `trap()` 时,当从用户模式中捕获一个<ruby>陷阱<rt>trap</rt></ruby>时获取锁。在检查 `tf_cs` 的低位比特,以确定一个陷阱是发生在用户模式还是内核模式时。
* 在 `env_run()` 中,在切换到用户模式之前释放锁。不能太早也不能太晚,否则你将可能会产生争用或死锁。
* 在 `i386_init()` 时,在 BSP 唤醒其它 CPU 之前获取锁。
* 在 `mp_main()` 时,在初始化 AP 之后获取锁,然后调用 `sched_yield()` 在这个 AP 上开始运行环境。
* 在 `trap()` 时,当从用户模式中捕获一个<ruby>陷阱<rt>trap</rt></ruby>时获取锁。在检查 `tf_cs` 的低位比特,以确定一个陷阱是发生在用户模式还是内核模式时。
* 在 `env_run()` 中,在切换到用户模式之前释放锁。不能太早也不能太晚,否则你将可能会产生争用或死锁。
> **练习 5**、在上面所描述的情况中,通过在合适的位置调用 `lock_kernel()``unlock_kernel()` 应用大内核锁。
```markdown
练习 5、在上面所描述的情况中通过在合适的位置调用 `lock_kernel()``unlock_kernel()` 应用大内核锁。
```
> 如果你的锁定是正确的,如何去测试它?实际上,到目前为止,还无法测试!但是在下一个练习中,你实现了调度之后,就可以测试了。
如果你的锁定是正确的,如何去测试它?实际上,到目前为止,还无法测试!但是在下一个练习中,你实现了调度之后,就可以测试了。
.
```
问题
2、看上去使用一个大内核锁可以保证在一个时间中只有一个 CPU 能够运行内核代码。为什么每个 CPU 仍然需要单独的内核栈?描述一下使用一个共享内核栈出现错误的场景,即便是在它使用了大内核锁保护的情况下。
```
> **问题 2**、看上去使用一个大内核锁,可以保证在一个时间中只有一个 CPU 能够运行内核代码。为什么每个 CPU 仍然需要单独的内核栈?描述一下使用一个共享内核栈出现错误的场景,即便是在它使用了大内核锁保护的情况下。
```
小挑战!大内核锁很简单,也易于使用。尽管如此,它消除了内核模式的所有并发。大多数现代操作系统使用不同的锁,一种称之为细粒度锁定的方法,去保护它们的共享的栈的不同部分。细粒度锁能够大幅提升性能,但是实现起来更困难并且易出错。如果你有足够的勇气,在 JOS 中删除大内核锁,去拥抱并发吧!
> **小挑战!**大内核锁很简单,也易于使用。尽管如此,它消除了内核模式的所有并发。大多数现代操作系统使用不同的锁,一种称之为细粒度锁定的方法,去保护它们的共享的栈的不同部分。细粒度锁能够大幅提升性能,但是实现起来更困难并且易出错。如果你有足够的勇气,在 JOS 中删除大内核锁,去拥抱并发吧!
由你来决定锁的粒度(一个锁保护的数据量)。给你一个提示,你可以考虑在 JOS 内核中使用一个自旋锁去确保你独占访问这些共享的组件:
> 由你来决定锁的粒度(一个锁保护的数据量)。给你一个提示,你可以考虑在 JOS 内核中使用一个自旋锁去确保你独占访问这些共享的组件:
* 页分配器
* 控制台驱动
* 调度器
* 你将在 Part C 中实现的进程间通讯IPC的状态
```
> * 页分配器
* 控制台驱动
* 调度器
* 你将在 Part C 中实现的进程间通讯IPC的状态
##### 循环调度
#### 循环调度
本实验中,你的下一个任务是去修改 JOS 内核以使它能够在多个环境之间以“循环”的方式去交替。JOS 中的循环调度工作方式如下:
* 在新的 `kern/sched.c` 中的 `sched_yield()` 函数负责去选择一个新环境来运行。它按顺序以循环的方式在数组 `envs[]` 中进行搜索,在前一个运行的环境之后开始(或如果之前没有运行的环境,就从数组起点开始),选择状态为 `ENV_RUNNABLE` 的第一个环境(查看 `inc/env.h`),并调用 `env_run()` 去跳转到那个环境。
* `sched_yield()` 必须做到,同一个时间在两个 CPU 上绝对不能运行相同的环境。它可以判断出一个环境正运行在一些 CPU可能是当前 CPU因为那个正在运行的环境的状态将是 `ENV_RUNNING`
* 我们已经为你实现了一个新的系统调用 `sys_yield()`,用户环境调用它去调用内核的 `sched_yield()` 函数,并因此将自愿把对 CPU 的控制禅让给另外的一个环境。
* 在新的 `kern/sched.c` 中的 `sched_yield()` 函数负责去选择一个新环境来运行。它按顺序以循环的方式在数组 `envs[]` 中进行搜索,在前一个运行的环境之后开始(或如果之前没有运行的环境,就从数组起点开始),选择状态为 `ENV_RUNNABLE` 的第一个环境(查看 `inc/env.h`),并调用 `env_run()` 去跳转到那个环境。
* `sched_yield()` 必须做到,同一个时间在两个 CPU 上绝对不能运行相同的环境。它可以判断出一个环境正运行在一些 CPU可能是当前 CPU因为那个正在运行的环境的状态将是 `ENV_RUNNING`
* 我们已经为你实现了一个新的系统调用 `sys_yield()`,用户环境调用它去调用内核的 `sched_yield()` 函数,并因此将自愿把对 CPU 的控制禅让给另外的一个环境。
> **练习 6**、像上面描述的那样,在 `sched_yield()` 中实现循环调度。不要忘了去修改 `syscall()` 以派发 `sys_yield()`
```c
练习 6、像上面描述的那样`sched_yield()` 中实现循环调度。不要忘了去修改 `syscall()` 以派发 `sys_yield()`
> 确保在 `mp_main` 中调用了 `sched_yield()`
确保在 `mp_main` 中调用了 `sched_yield()`
> 修改 `kern/init.c` 去创建三个(或更多个!)运行程序 `user/yield.c`的环境
修改 `kern/init.c` 去创建三个(或更多个!)运行程序 `user/yield.c`的环境
> 运行 `make qemu`。在它终止之前,你应该会看到像下面这样,在环境之间来回切换了五次
运行 `make qemu`。在它终止之前,你应该会看到像下面这样,在环境之间来回切换了五次
> 也可以使用几个 CPU 来测试:`make qemu CPUS=2`
也可以使用几个 CPU 来测试make qemu CPUS=2。
...
Hello, I am environment 00001000.
Hello, I am environment 00001001.
Hello, I am environment 00001002.
Back in environment 00001000, iteration 0.
Back in environment 00001001, iteration 0.
Back in environment 00001002, iteration 0.
Back in environment 00001000, iteration 1.
Back in environment 00001001, iteration 1.
Back in environment 00001002, iteration 1.
...
在程序 `yield` 退出之后,系统中将没有可运行的环境,调度器应该会调用 JOS 内核监视器。如果它什么也没有发生,那么你应该在继续之前修复你的代码。
>```
...
Hello, I am environment 00001000.
Hello, I am environment 00001001.
Hello, I am environment 00001002.
Back in environment 00001000, iteration 0.
Back in environment 00001001, iteration 0.
Back in environment 00001002, iteration 0.
Back in environment 00001000, iteration 1.
Back in environment 00001001, iteration 1.
Back in environment 00001002, iteration 1.
...
```
```c
问题
3、在你实现的 `env_run()` 中,你应该会调用 `lcr3()`。在调用 `lcr3()` 的之前和之后,你的代码引用(至少它应该会)变量 `e`,它是 `env_run` 的参数。在加载 `%cr3` 寄存器时MMU 使用的地址上下文将马上被改变。但一个虚拟地址(即 `e`)相对一个给定的地址上下文是有意义的 —— 地址上下文指定了物理地址到那个虚拟地址的映射。为什么指针 `e` 在地址切换之前和之后被解除引用?
4、无论何时内核从一个环境切换到另一个环境它必须要确保旧环境的寄存器内容已经被保存以便于它们稍后能够正确地还原。为什么这种事件发生在什么地方
```
> 在程序 `yield` 退出之后,系统中将没有可运行的环境,调度器应该会调用 JOS 内核监视器。如果它什么也没有发生,那么你应该在继续之前修复你的代码。
```c
小挑战!给内核添加一个小小的调度策略,比如一个固定优先级的调度器,它将会给每个环境分配一个优先级,并且在执行中,较高优先级的环境总是比低优先级的环境优先被选定。如果你想去冒险一下,尝试实现一个类 Unix 的、优先级可调整的调度器,或者甚至是一个彩票调度器或跨步调度器。(可以在 Google 中查找“彩票调度”和“跨步调度”的相关资料)
写一个或两个测试程序,去测试你的调度算法是否工作正常(即,正确的算法能够按正确的次序运行)。如果你实现了本实验的 Part B 和 Part C 部分的 `fork()` 和 IPC写这些测试程序可能会更容易。
```
> **问题 3**、在你实现的 `env_run()` 中,你应该会调用 `lcr3()`。在调用 `lcr3()` 的之前和之后,你的代码引用(至少它应该会)变量 `e`,它是 `env_run` 的参数。在加载 `%cr3` 寄存器时MMU 使用的地址上下文将马上被改变。但一个虚拟地址(即 `e`)相对一个给定的地址上下文是有意义的 —— 地址上下文指定了物理地址到那个虚拟地址的映射。为什么指针 `e` 在地址切换之前和之后被解除引用?
```markdown
小挑战!目前的 JOS 内核还不能应用到使用了 x87 协处理器、MMX 指令集、或流式 SIMD 扩展SSE的 x86 处理器上。扩展数据结构 `Env` 去提供一个能够保存处理器的浮点状态的地方,并且扩展上下文切换代码,当从一个环境切换到另一个环境时,能够保存和还原正确的状态。`FXSAVE` 和 `FXRSTOR` 指令或许对你有帮助,但是需要注意的是,这些指令在旧的 x86 用户手册上没有,因为它是在较新的处理器上引入的。写一个用户级的测试程序,让它使用浮点做一些很酷的事情。
```
.
##### 创建环境的系统调用
> **问题 4**、无论何时,内核从一个环境切换到另一个环境,它必须要确保旧环境的寄存器内容已经被保存,以便于它们稍后能够正确地还原。为什么?这种事件发生在什么地方?
.
> 小挑战!给内核添加一个小小的调度策略,比如一个固定优先级的调度器,它将会给每个环境分配一个优先级,并且在执行中,较高优先级的环境总是比低优先级的环境优先被选定。如果你想去冒险一下,尝试实现一个类 Unix 的、优先级可调整的调度器,或者甚至是一个彩票调度器或跨步调度器。(可以在 Google 中查找“彩票调度”和“跨步调度”的相关资料)
> 写一个或两个测试程序,去测试你的调度算法是否工作正常(即,正确的算法能够按正确的次序运行)。如果你实现了本实验的 Part B 和 Part C 部分的 `fork()` 和 IPC写这些测试程序可能会更容易。
.
> 小挑战!目前的 JOS 内核还不能应用到使用了 x87 协处理器、MMX 指令集、或流式 SIMD 扩展SSE的 x86 处理器上。扩展数据结构 `Env` 去提供一个能够保存处理器的浮点状态的地方,并且扩展上下文切换代码,当从一个环境切换到另一个环境时,能够保存和还原正确的状态。`FXSAVE` 和 `FXRSTOR` 指令或许对你有帮助,但是需要注意的是,这些指令在旧的 x86 用户手册上没有,因为它是在较新的处理器上引入的。写一个用户级的测试程序,让它使用浮点做一些很酷的事情。
#### 创建环境的系统调用
虽然你的内核现在已经有了在多个用户级环境之间切换的功能,但是由于内核初始化设置的原因,它在运行环境时仍然是受限的。现在,你需要去实现必需的 JOS 系统调用,以允许用户环境去创建和启动其它的新用户环境。
@ -229,34 +214,35 @@ Unix 提供了 `fork()` 系统调用作为它的进程创建原语。Unix 的 `f
为创建一个用户模式下的新的环境,你将要提供一个不同的、更原始的 JOS 系统调用集。使用这些系统调用,除了其它类型的环境创建之外,你可以在用户空间中实现一个完整的类 Unix 的 `fork()`。你将要为 JOS 编写的新的系统调用如下:
* `sys_exofork`
这个系统调用创建一个新的空白的环境:在它的地址空间的用户部分什么都没有映射,并且它也不能运行。这个新的环境与 `sys_exofork` 调用时创建它的父环境的寄存器状态完全相同。在父进程中,`sys_exofork` 将返回新创建进程的 `envid_t`(如果环境分配失败的话,返回的是一个负的错误代码)。在子进程中,它将返回 0。因为子进程从一开始就被标记为不可运行在子进程中`sys_exofork` 将并不真的返回,直到它的父进程使用 .... 显式地将子进程标记为可运行之前。)
* `sys_env_set_status`
设置指定的环境状态为 `ENV_RUNNABLE``ENV_NOT_RUNNABLE`。这个系统调用一般是在,一个新环境的地址空间和寄存器状态已经完全初始化完成之后,用于去标记一个准备去运行的新环境。
* `sys_page_alloc`
分配一个物理内存页,并映射它到一个给定的环境地址空间中、给定的一个虚拟地址上。
* `sys_page_map`
从一个环境的地址空间中复制一个页映射(不是页内容!)到另一个环境的地址空间中,保持一个内存共享,以便于新的和旧的映射共同指向到同一个物理内存页。
* `sys_page_unmap`
在一个给定的环境中,取消映射一个给定的已映射的虚拟地址。
* `sys_exofork`
这个系统调用创建一个新的空白的环境:在它的地址空间的用户部分什么都没有映射,并且它也不能运行。这个新的环境与 `sys_exofork` 调用时创建它的父环境的寄存器状态完全相同。在父进程中,`sys_exofork` 将返回新创建进程的 `envid_t`(如果环境分配失败的话,返回的是一个负的错误代码)。在子进程中,它将返回 0。因为子进程从一开始就被标记为不可运行在子进程中`sys_exofork` 将并不真的返回,直到它的父进程使用 .... 显式地将子进程标记为可运行之前。)
* `sys_env_set_status`
设置指定的环境状态为 `ENV_RUNNABLE``ENV_NOT_RUNNABLE`。这个系统调用一般是在,一个新环境的地址空间和寄存器状态已经完全初始化完成之后,用于去标记一个准备去运行的新环境。
* `sys_page_alloc`
分配一个物理内存页,并映射它到一个给定的环境地址空间中、给定的一个虚拟地址上。
* `sys_page_map`
从一个环境的地址空间中复制一个页映射(不是页内容!)到另一个环境的地址空间中,保持一个内存共享,以便于新的和旧的映射共同指向到同一个物理内存页。
* `sys_page_unmap`
在一个给定的环境中,取消映射一个给定的已映射的虚拟地址。
上面所有的系统调用都接受环境 ID 作为参数JOS 内核支持一个约定,那就是用值 “0” 来表示“当前环境”。这个约定在 `kern/env.c` 中的 `envid2env()` 中实现的。
在我们的 `user/dumbfork.c` 中的测试程序里,提供了一个类 Unix 的 `fork()` 的非常原始的实现。这个测试程序使用了上面的系统调用,去创建和运行一个复制了它自己地址空间的子环境。然后,这两个环境像前面的练习那样使用 `sys_yield` 来回切换,父进程在迭代 10 次后退出,而子进程在迭代 20 次后退出。
```c
练习 7、在 `kern/syscall.c` 中实现上面描述的系统调用,并确保 `syscall()` 能调用它们。你将需要使用 `kern/pmap.c``kern/env.c` 中的多个函数,尤其是要用到 `envid2env()`。目前,每当你调用 `envid2env()` 时,在 `checkperm` 中传递参数 1。你务必要做检查任何无效的系统调用参数在那个案例中就返回了 `-E_INVAL`。使用 `user/dumbfork` 测试你的 JOS 内核,并在继续之前确保它运行正常。
```
> **练习 7**、在 `kern/syscall.c` 中实现上面描述的系统调用,并确保 `syscall()` 能调用它们。你将需要使用 `kern/pmap.c``kern/env.c` 中的多个函数,尤其是要用到 `envid2env()`。目前,每当你调用 `envid2env()` 时,在 `checkperm` 中传递参数 1。你务必要做检查任何无效的系统调用参数在那个案例中就返回了 `-E_INVAL`。使用 `user/dumbfork` 测试你的 JOS 内核,并在继续之前确保它运行正常。
```c
小挑战!添加另外的系统调用,必须能够读取已存在的、所有的、环境的重要状态,以及设置它们。然后实现一个能够 fork 出子环境的用户模式程序,运行它一小会(即,迭代几次 `sys_yield()`),然后取得几张屏幕截图或子环境的检查点,然后运行子环境一段时间,然后还原子环境到检查点时的状态,然后从这里继续开始。这样,你就可以有效地从一个中间状态“回放”了子环境的运行。确保子环境与用户使用 `sys_cgetc()``readline()` 执行了一些交互,这样,那个用户就能够查看和突变它的内部状态,并且你可以通过给子环境给定一个选择性遗忘的状况,来验证你的检查点/重启动的有效性,使它“遗忘”了在某些点之前发生的事情。
```
.
> **小挑战!**添加另外的系统调用,必须能够读取已存在的、所有的、环境的重要状态,以及设置它们。然后实现一个能够 fork 出子环境的用户模式程序,运行它一小会(即,迭代几次 `sys_yield()`),然后取得几张屏幕截图或子环境的检查点,然后运行子环境一段时间,然后还原子环境到检查点时的状态,然后从这里继续开始。这样,你就可以有效地从一个中间状态“回放”了子环境的运行。确保子环境与用户使用 `sys_cgetc()` 或 `readline()` 执行了一些交互,这样,那个用户就能够查看和突变它的内部状态,并且你可以通过给子环境给定一个选择性遗忘的状况,来验证你的检查点/重启动的有效性,使它“遗忘”了在某些点之前发生的事情。
到此为止,已经完成了本实验的 Part A 部分;在你运行 `make grade` 之前确保它通过了所有的 Part A 的测试,并且和以往一样,使用 `make handin` 去提交它。如果你想尝试找出为什么一些特定的测试是失败的,可以运行 `run ./grade-lab4 -v`,它将向你展示内核构建的输出,和测试失败时的 QEMU 运行情况。当测试失败时,这个脚本将停止运行,然后你可以去检查 `jos.out` 的内容,去查看内核真实的输出内容。
#### Part B写时复制 Fork
### Part B写时复制 Fork
正如在前面提到过的Unix 提供 `fork()` 系统调用作为它主要的进程创建原语。`fork()` 系统调用通过复制调用进程(父进程)的地址空间来创建一个新进程(子进程)。
@ -264,11 +250,11 @@ xv6 Unix 的 `fork()` 从父进程的页上复制所有数据,然后将它分
但是,一个对 `fork()` 的调用后,经常是紧接着几乎立即在子进程中有一个到 `exec()` 的调用,它使用一个新程序来替换子进程的内存。这是 shell 默认去做的事,在这种情况下,在复制父进程地址空间上花费的时间是非常浪费的,因为在调用 `exec()` 之前,子进程使用的内存非常少。
基于这个原因Unix 的最新版本利用了虚拟内存硬件的优势,允许父进程和子进程去共享映射到它们各自地址空间上的内存,直到其中一个进程真实地修改了它们为止。这个技术就是众所周知的“写时复制”。为实现这一点,在 `fork()`内核将复制从父进程到子进程的地址空间的映射而不是所映射的页的内容并且同时设置正在共享中的页为只读。当两个进程中的其中一个尝试去写入到它们共享的页上时进程将产生一个页故障。在这时Unix 内核才意识到那个页实际上是“虚拟的”或“写时复制”的副本,然后它生成一个新的、私有的、那个发生页故障的进程可写的、页的副本。在这种方式中,个人的页的内容并不进行真实地复制,直到它们真正进行写入时才进行复制。这种优化使得一个`fork()` 后在子进程中跟随一个 `exec()` 变得代价很低了:子进程在调用 `exec()` 时或许仅需要复制一个页(它的栈的当前页)。
基于这个原因Unix 的最新版本利用了虚拟内存硬件的优势,允许父进程和子进程去共享映射到它们各自地址空间上的内存,直到其中一个进程真实地修改了它们为止。这个技术就是众所周知的“写时复制”。为实现这一点,在 `fork()`内核将复制从父进程到子进程的地址空间的映射而不是所映射的页的内容并且同时设置正在共享中的页为只读。当两个进程中的其中一个尝试去写入到它们共享的页上时进程将产生一个页故障。在这时Unix 内核才意识到那个页实际上是“虚拟的”或“写时复制”的副本,然后它生成一个新的、私有的、那个发生页故障的进程可写的、页的副本。在这种方式中,个人的页的内容并不进行真实地复制,直到它们真正进行写入时才进行复制。这种优化使得一个`fork()` 后在子进程中跟随一个 `exec()` 变得代价很低了:子进程在调用 `exec()` 时或许仅需要复制一个页(它的栈的当前页)。
在本实验的下一段中,你将实现一个带有“写时复制”的“真正的”类 Unix 的 `fork()`,来作为一个常规的用户空间库。在用户空间中实现 `fork()` 和写时复制有一个好处就是,让内核始终保持简单,并且因此更不易出错。它也让个别的用户模式程序在 `fork()` 上定义了它们自己的语义。一个有略微不同实现的程序(例如,代价昂贵的、总是复制的 `dumbfork()` 版本,或父子进程真实共享内存的后面的那一个),它自己可以很容易提供。
##### 用户级页故障处理
#### 用户级页故障处理
一个用户级写时复制 `fork()` 需要知道关于在写保护页上的页故障相关的信息,因此,这是你首先需要去实现的东西。对用户级页故障处理来说,写时复制仅是众多可能的用途之一。
@ -276,15 +262,14 @@ xv6 Unix 的 `fork()` 从父进程的页上复制所有数据,然后将它分
内核跟踪有大量的信息,与传统的 Unix 方法不同,你将决定在每个用户空间中关于每个页故障应该做的事。用户空间中的 bug 危害都较小。这种设计带来了额外的好处,那就是允许程序员在定义它们的内存区域时,会有很好的灵活性;对于映射和访问基于磁盘文件系统上的文件时,你应该使用后面的用户级页故障处理。
###### 设置页故障服务程序
##### 设置页故障服务程序
为了处理它自己的页故障,一个用户环境将需要在 JOS 内核上注册一个页故障服务程序入口。用户环境通过新的 `sys_env_set_pgfault_upcall` 系统调用来注册它的页故障入口。我们给结构 `Env` 增加了一个新的成员 `env_pgfault_upcall`,让它去记录这个信息。
```markdown
练习 8、实现 `sys_env_set_pgfault_upcall` 系统调用。当查找目标环境的环境 ID 时,一定要确认启用了权限检查,因为这是一个“危险的”系统调用。
> **练习 8**、实现 `sys_env_set_pgfault_upcall` 系统调用。当查找目标环境的环境 ID 时,一定要确认启用了权限检查,因为这是一个“危险的”系统调用。
```
###### 在用户环境中的正常和异常栈
##### 在用户环境中的正常和异常栈
在正常运行期间JOS 中的一个用户环境运行在正常的用户栈上:它的 `ESP` 寄存器开始指向到 `USTACKTOP`,而它所推送的栈数据将驻留在 `USTACKTOP-PGSIZE``USTACKTOP-1`(含)之间的页上。但是,当在用户模式中发生页故障时,内核将在一个不同的栈上重新启动用户环境,运行一个用户级页故障指定的服务程序,即用户异常栈。其它,我们将让 JOS 内核为用户环境实现自动的“栈切换”当从用户模式转换到内核模式时x86 处理器就以大致相同的方式为 JOS 实现了栈切换。
@ -292,7 +277,7 @@ JOS 用户异常栈也是一个页的大小,并且它的顶部被定义在虚
每个想去支持用户级页故障处理的用户环境,都需要为它自己的异常栈使用在 Part A 中介绍的 `sys_page_alloc()` 系统调用去分配内存。
###### 调用用户页故障服务程序
##### 调用用户页故障服务程序
现在,你需要去修改 `kern/trap.c` 中的页故障处理代码,以能够处理接下来在用户模式中发生的页故障。我们将故障发生时用户环境的状态称之为捕获时状态。
@ -322,25 +307,20 @@ JOS 用户异常栈也是一个页的大小,并且它的顶部被定义在虚
去测试 `tf->tf_esp` 是否已经在用户异常栈上准备好,可以去检查它是否在 `UXSTACKTOP-PGSIZE``UXSTACKTOP-1`(含)的范围内。
```markdown
练习 9、实现在 `kern/trap.c` 中的 `page_fault_handler` 的代码,要求派发页故障到用户模式故障服务程序上。在写入到异常栈时,一定要采取适当的预防措施。(如果用户环境运行时溢出了异常栈,会发生什么事情?)
```
> **练习 9**、实现在 `kern/trap.c` 中的 `page_fault_handler` 的代码,要求派发页故障到用户模式故障服务程序上。在写入到异常栈时,一定要采取适当的预防措施。(如果用户环境运行时溢出了异常栈,会发生什么事情?)
###### 用户模式页故障入口点
##### 用户模式页故障入口点
接下来,你需要去实现汇编程序,它将调用 C 页故障服务程序,并在原始的故障指令处恢复程序运行。这个汇编程序是一个故障服务程序,它由内核使用 `sys_env_set_pgfault_upcall()` 来注册。
```markdown
练习 10、实现在 `lib/pfentry.S` 中的 `_pgfault_upcall` 程序。最有趣的部分是返回到用户代码中产生页故障的原始位置。你将要直接返回到那里,不能通过内核返回。最难的部分是同时切换栈和重新加载 EIP。
```
> **练习 10**、实现在 `lib/pfentry.S` 中的 `_pgfault_upcall` 程序。最有趣的部分是返回到用户代码中产生页故障的原始位置。你将要直接返回到那里,不能通过内核返回。最难的部分是同时切换栈和重新加载 EIP。
最后,你需要去实现用户级页故障处理机制的 C 用户库。
```c
练习 11、完成 `lib/pgfault.c` 中的 `set_pgfault_handler()`
> **练习 11**、完成 `lib/pgfault.c` 中的 `set_pgfault_handler()`
```
###### 测试
##### 测试
运行 `user/faultread`make run-faultread你应该会看到
@ -376,7 +356,7 @@ JOS 用户异常栈也是一个页的大小,并且它的顶部被定义在虚
[00001000] free env 00001000
```
如果你只看到第一个 "this string” 行,意味着你没有正确地处理递归页故障。
如果你只看到第一个 this string” 行,意味着你没有正确地处理递归页故障。
运行 `user/faultallocbad` 你应该会看到:
@ -389,11 +369,9 @@ JOS 用户异常栈也是一个页的大小,并且它的顶部被定义在虚
确保你理解了为什么 `user/faultalloc``user/faultallocbad` 的行为是不一样的。
```markdown
小挑战!扩展你的内核,让它不仅是页故障,而是在用户空间中运行的代码能够产生的所有类型的处理器异常,都能够被重定向到一个用户模式中的异常服务程序上。写出用户模式测试程序,去测试各种各样的用户模式异常处理,比如除零错误、一般保护故障、以及非法操作码。
```
> **小挑战!**扩展你的内核,让它不仅是页故障,而是在用户空间中运行的代码能够产生的所有类型的处理器异常,都能够被重定向到一个用户模式中的异常服务程序上。写出用户模式测试程序,去测试各种各样的用户模式异常处理,比如除零错误、一般保护故障、以及非法操作码。
##### 实现写时复制 Fork
#### 实现写时复制 Fork
现在,你有个内核功能要去实现,那就是在用户空间中完整地实现写时复制 `fork()`
@ -401,38 +379,29 @@ JOS 用户异常栈也是一个页的大小,并且它的顶部被定义在虚
`fork()` 的基本控制流如下:
1. 父环境使用你在上面实现的 `set_pgfault_handler()` 函数,安装 `pgfault()` 作为 C 级页故障服务程序。
2. 父环境调用 `sys_exofork()` 去创建一个子环境。
3. 在它的地址空间中,低于 UTOP 位置的、每个可写入页、或写时复制页上,父环境调用 `duppage` 后,它应该会映射页写时复制到子环境的地址空间中,然后在它自己的地址空间中重新映射页写时复制。[ 注意:这里的顺序很重要(即,在父环境中标记之前,先在子环境中标记该页为 COW你能明白是为什么吗尝试去想一个具体的案例将顺序颠倒一下会发生什么样的问题。] `duppage` 把两个 PTE 都设置了,致使那个页不可写入,并且在 "avail” 字段中通过包含 `PTE_COW` 来从真正的只读页中区分写时复制页。
然而异常栈是不能通过这种方式重映射的。对于异常栈,你需要在子环境中分配一个新页。因为页故障服务程序不能做真实的复制,并且页故障服务程序是运行在异常栈上的,异常栈不能进行写时复制:那么谁来复制它呢?
`fork()` 也需要去处理存在的页,但不能写入或写时复制。
4. 父环境为子环境设置了用户页故障入口点,让它看起来像它自己的一样。
5. 现在,子环境准备去运行,所以父环境标记它为可运行。
1. 父环境使用你在上面实现的 `set_pgfault_handler()` 函数,安装 `pgfault()` 作为 C 级页故障服务程序。
2. 父环境调用 `sys_exofork()` 去创建一个子环境。
3. 在它的地址空间中,低于 UTOP 位置的、每个可写入页、或写时复制页上,父环境调用 `duppage` 后,它应该会映射页写时复制到子环境的地址空间中,然后在它自己的地址空间中重新映射页写时复制。[ 注意:这里的顺序很重要(即,在父环境中标记之前,先在子环境中标记该页为 COW你能明白是为什么吗尝试去想一个具体的案例将顺序颠倒一下会发生什么样的问题。] `duppage` 把两个 PTE 都设置了,致使那个页不可写入,并且在 "avail” 字段中通过包含 `PTE_COW` 来从真正的只读页中区分写时复制页。
然而异常栈是不能通过这种方式重映射的。对于异常栈,你需要在子环境中分配一个新页。因为页故障服务程序不能做真实的复制,并且页故障服务程序是运行在异常栈上的,异常栈不能进行写时复制:那么谁来复制它呢?
`fork()` 也需要去处理存在的页,但不能写入或写时复制。
4. 父环境为子环境设置了用户页故障入口点,让它看起来像它自己的一样。
5. 现在,子环境准备去运行,所以父环境标记它为可运行。
每次其中一个环境写一个还没有写入的写时复制页时,它将产生一个页故障。下面是用户页故障服务程序的控制流:
1. 内核传递页故障到 `_pgfault_upcall`,它调用 `fork()``pgfault()` 服务程序。
2. `pgfault()` 检测到那个故障是一个写入(在错误代码中检查 `FEC_WR`),然后将那个页的 PTE 标记为 `PTE_COW`。如果不是一个写入,则崩溃。
3. `pgfault()` 在一个临时位置分配一个映射的新页,并将故障页的内容复制进去。然后,故障服务程序以读取/写入权限映射新页到合适的地址,替换旧的只读映射。
1. 内核传递页故障到 `_pgfault_upcall`,它调用 `fork()``pgfault()` 服务程序。
2. `pgfault()` 检测到那个故障是一个写入(在错误代码中检查 `FEC_WR`),然后将那个页的 PTE 标记为 `PTE_COW`。如果不是一个写入,则崩溃。
3. `pgfault()` 在一个临时位置分配一个映射的新页,并将故障页的内容复制进去。然后,故障服务程序以读取/写入权限映射新页到合适的地址,替换旧的只读映射。
对于上面的几个操作,用户级 `lib/fork.c` 代码必须查询环境的页表(即,那个页的 PTE 是否标记为 `PET_COW`)。为此,内核在 `UVPT` 位置精确地映射环境的页表。它使用一个 [聪明的映射技巧][1] 去标记它,以使用户代码查找 PTE 时更容易。`lib/entry.S` 设置 `uvpt``uvpd`,以便于你能够在 `lib/fork.c` 中轻松查找页表信息。
```c
练习 12、在 `lib/fork.c` 中实现 `fork`、`duppage` 和 `pgfault`
> **练习 12**、在 `lib/fork.c` 中实现 `fork`、`duppage` 和 `pgfault`
使用 `forktree` 程序测试你的代码。它应该会产生下列的信息,在信息中会有 'new env'、'free env'、和 'exiting gracefully' 这样的字眼。信息可能不是按如下的顺序出现的,并且环境 ID 也可能不一样。
> 使用 `forktree` 程序测试你的代码。它应该会产生下列的信息,在信息中会有 'new env'、'free env'、和 'exiting gracefully' 这样的字眼。信息可能不是按如下的顺序出现的,并且环境 ID 也可能不一样。
>```
1000: I am ''
1001: I am '0'
2000: I am '00'
@ -450,31 +419,32 @@ JOS 用户异常栈也是一个页的大小,并且它的顶部被定义在虚
1006: I am '101'
```
```c
小挑战!实现一个名为 `sfork()` 的共享内存的 `fork()`。这个版本的 `sfork()` 中,父子环境共享所有的内存页(因此,一个环境中对内存写入,就会改变另一个环境数据),除了在栈区域中的页以外,它应该使用写时复制来处理这些页。修改 `user/forktree.c` 去使用 `sfork()` 而是不常见的 `fork()`。另外,你在 Part C 中实现了 IPC 之后,使用你的 `sfork()` 去运行 `user/pingpongs`。你将找到提供全局指针 `thisenv` 功能的一个新方式。
```
.
```markdown
小挑战!你实现的 `fork` 将产生大量的系统调用。在 x86 上,使用中断切换到内核模式将产生较高的代价。增加系统调用接口,以便于它能够一次发送批量的系统调用。然后修改 `fork` 去使用这个接口。
你的新的 `fork` 有多快?
> **小挑战!**实现一个名为 `sfork()` 的共享内存的 `fork()`。这个版本的 `sfork()` 中,父子环境共享所有的内存页(因此,一个环境中对内存写入,就会改变另一个环境数据),除了在栈区域中的页以外,它应该使用写时复制来处理这些页。修改 `user/forktree.c` 去使用 `sfork()` 而是不常见的 `fork()`。另外,你在 Part C 中实现了 IPC 之后,使用你的 `sfork()` 去运行 `user/pingpongs`。你将找到提供全局指针 `thisenv` 功能的一个新方式。
你可以用一个分析来论证,批量提交对你的 `fork` 的性能改变,以它来(粗略地)回答这个问题:使用一个 `int 0x30` 指令的代价有多高?在你的 `fork` 中运行了多少次 `int 0x30` 指令?访问 `TSS` 栈切换的代价高吗?等待 ...
.
或者,你可以在真实的硬件上引导你的内核,并且真实地对你的代码做基准测试。查看 `RDTSC`(读取时间戳计数器)指令,它的定义在 IA32 手册中它计数自上一次处理器重置以来流逝的时钟周期数。QEMU 并不能真实地模拟这个指令(它能够计数运行的虚拟指令数量,或使用主机的 TSC但是这两种方式都不能反映真实的 CPU 周期数)。
```
> **小挑战!**你实现的 `fork` 将产生大量的系统调用。在 x86 上,使用中断切换到内核模式将产生较高的代价。增加系统调用接口,以便于它能够一次发送批量的系统调用。然后修改 `fork` 去使用这个接口。
> 你的新的 `fork` 有多快?
> 你可以用一个分析来论证,批量提交对你的 `fork` 的性能改变,以它来(粗略地)回答这个问题:使用一个 `int 0x30` 指令的代价有多高?在你的 `fork` 中运行了多少次 `int 0x30` 指令?访问 `TSS` 栈切换的代价高吗?等待 ...
> 或者,你可以在真实的硬件上引导你的内核,并且真实地对你的代码做基准测试。查看 `RDTSC`(读取时间戳计数器)指令,它的定义在 IA32 手册中它计数自上一次处理器重置以来流逝的时钟周期数。QEMU 并不能真实地模拟这个指令(它能够计数运行的虚拟指令数量,或使用主机的 TSC但是这两种方式都不能反映真实的 CPU 周期数)。
到此为止Part B 部分结束了。在你运行 `make grade` 之前,确保你通过了所有的 Part B 部分的测试。和以前一样,你可以使用 `make handin` 去提交你的实验。
#### Part C抢占式多任务处理和进程间通讯IPC
### Part C抢占式多任务处理和进程间通讯IPC
在实验 4 的最后部分,你将修改内核去抢占不配合的环境,并允许环境之间显式地传递消息。
##### 时钟中断和抢占
#### 时钟中断和抢占
运行测试程序 `user/spin`。这个测试程序 fork 出一个子环境,它控制了 CPU 之后,就永不停歇地运转起来。无论是父环境还是内核都不能回收对 CPU 的控制。从用户模式环境中保护系统免受 bug 或恶意代码攻击的角度来看,这显然不是个理想的状态,因为任何用户模式环境都能够通过简单的无限循环,并永不归还 CPU 控制权的方式,让整个系统处于暂停状态。为了允许内核去抢占一个运行中的环境,从其中夺回对 CPU 的控制权,我们必须去扩展 JOS 内核,以支持来自硬件时钟的外部硬件中断。
###### 中断规则
##### 中断规则
外部中断(即:设备中断)被称为 IRQ。现在有 16 个可能出现的 IRQ编号 0 到 15。从 IRQ 号到 IDT 条目的映射是不固定的。在 `picirq.c` 中的 `pic_init` 映射 IRQ 0 - 15 到 IDT 条目 `IRQ_OFFSET``IRQ_OFFSET+15`
@ -484,31 +454,27 @@ JOS 用户异常栈也是一个页的大小,并且它的顶部被定义在虚
处于用户环境中时,你将要确保 `FL_IF` 标志被设置,以便于出现一个中断时,它能够通过处理器来传递,让你的中断代码来处理。否则,中断将被屏蔽或忽略,直到中断被重新打开后。我们使用引导加载程序的第一个指令去屏蔽中断,并且到目前为止,还没有去重新打开它们。
```markdown
练习 13、修改 `kern/trapentry.S``kern/trap.c` 去初始化 IDT 中的相关条目,并为 IRQ 0 到 15 提供服务程序。然后修改 `kern/env.c` 中的 `env_alloc()` 的代码,以确保在用户环境中,中断总是打开的。
> **练习 13**、修改 `kern/trapentry.S``kern/trap.c` 去初始化 IDT 中的相关条目,并为 IRQ 0 到 15 提供服务程序。然后修改 `kern/env.c` 中的 `env_alloc()` 的代码,以确保在用户环境中,中断总是打开的。
另外,在 `sched_halt()` 中取消注释 `sti` 指令,以便于空闲的 CPU 取消屏蔽中断。
> 另外,在 `sched_halt()` 中取消注释 `sti` 指令,以便于空闲的 CPU 取消屏蔽中断。
当调用一个硬件中断服务程序时,处理器不会推送一个错误代码。在这个时候,你可能需要重新阅读 [80386 参考手册][2] 的 9.2 节,或 [IA-32 Intel 架构软件开发者手册 卷 3][3] 的 5.8 节。
> 当调用一个硬件中断服务程序时,处理器不会推送一个错误代码。在这个时候,你可能需要重新阅读 [80386 参考手册][2] 的 9.2 节,或 [IA-32 Intel 架构软件开发者手册 卷 3][3] 的 5.8 节。
在完成这个练习后,如果你在你的内核上使用任意的测试程序去持续运行(即:`spin`),你应该会看到内核输出中捕获的硬件中断的捕获帧。虽然在处理器上已经打开了中断,但是 JOS 并不能处理它们,因此,你应该会看到在当前运行的用户环境中每个中断的错误属性并被销毁,最终环境会被销毁并进入到监视器中。
```
> 在完成这个练习后,如果你在你的内核上使用任意的测试程序去持续运行(即:`spin`),你应该会看到内核输出中捕获的硬件中断的捕获帧。虽然在处理器上已经打开了中断,但是 JOS 并不能处理它们,因此,你应该会看到在当前运行的用户环境中每个中断的错误属性并被销毁,最终环境会被销毁并进入到监视器中。
###### 处理时钟中断
##### 处理时钟中断
`user/spin` 程序中,子环境首先运行之后,它只是进入一个高速循环中,并且内核再无法取得 CPU 控制权。我们需要对硬件编程,定期产生时钟中断,它将强制将 CPU 控制权返还给内核,在内核中,我们就能够将控制权切换到另外的用户环境中。
我们已经为你写好了对 `lapic_init``pic_init`(来自 `init.c` 中的 `i386_init`)的调用,它将设置时钟和中断控制器去产生中断。现在,你需要去写代码来处理这些中断。
```markdown
练习 14、修改内核的 `trap_dispatch()` 函数,以便于在时钟中断发生时,它能够调用 `sched_yield()` 去查找和运行一个另外的环境。
> **练习 14**、修改内核的 `trap_dispatch()` 函数,以便于在时钟中断发生时,它能够调用 `sched_yield()` 去查找和运行一个另外的环境。
现在,你应该能够用 `user/spin` 去做测试了:父环境应该会 fork 出子环境,`sys_yield()` 到它许多次,但每次切换之后,将重新获得对 CPU 的控制权,最后杀死子环境后优雅地终止。
```
> 现在,你应该能够用 `user/spin` 去做测试了:父环境应该会 fork 出子环境,`sys_yield()` 到它许多次,但每次切换之后,将重新获得对 CPU 的控制权,最后杀死子环境后优雅地终止。
这是做回归测试的好机会。确保你没有弄坏本实验的前面部分,确保打开中断能够正常工作(即: `forktree`)。另外,尝试使用 ` make CPUS=2 target` 在多个 CPU 上运行它。现在,你应该能够通过 `stresssched` 测试。可以运行 `make grade` 去确认。现在,你的得分应该是 65 分了(总分为 80
##### 进程间通讯IPC
#### 进程间通讯IPC
(严格来说,在 JOS 中这是“环境间通讯” 或 “IEC”但所有人都称它为 IPC因此我们使用标准的术语。
@ -516,13 +482,13 @@ JOS 用户异常栈也是一个页的大小,并且它的顶部被定义在虚
进程间通讯有许多模型。关于哪个模型最好的争论从来没有停止过。我们不去参与这种争论。相反,我们将要实现一个简单的 IPC 机制,然后尝试使用它。
###### JOS 中的 IPC
##### JOS 中的 IPC
你将要去实现另外几个 JOS 内核的系统调用,由它们共同来提供一个简单的进程间通讯机制。你将要实现两个系统调用,`sys_ipc_recv` 和 `sys_ipc_try_send`。然后你将要实现两个库去封装 `ipc_recv``ipc_send`
用户环境可以使用 JOS 的 IPC 机制相互之间发送 “消息” 到每个其它环境,这些消息有两部分组成:一个单个的 32 位值,和可选的一个单个页映射。允许环境在消息中传递页映射,提供了一个高效的方式,传输比一个仅适合单个的 32 位整数更多的数据,并且也允许环境去轻松地设置安排共享内存。
###### 发送和接收消息
##### 发送和接收消息
一个环境通过调用 `sys_ipc_recv` 去接收消息。这个系统调用将取消对当前环境的调度,并且不会再次去运行它,直到消息被接收为止。当一个环境正在等待接收一个消息时,任何其它环境都能够给它发送一个消息 — 而不仅是一个特定的环境,而且不仅是与接收环境有父子关系的环境。换句话说,你在 Part A 中实现的权限检查将不会应用到 IPC 上,因为 IPC 系统调用是经过慎重设计的,因此可以认为它是“安全的”:一个环境并不能通过给它发送消息导致另一个环境发生故障(除非目标环境也存在 Bug
@ -532,7 +498,7 @@ JOS 用户异常栈也是一个页的大小,并且它的顶部被定义在虚
同样,一个库函数 `ipc_send` 将去不停地调用 `sys_ipc_try_send` 来发送消息,直到发送成功为止。
###### 转移页
##### 转移页
当一个环境使用一个有效的 `dstva` 参数(低于 `UTOP`)去调用 `sys_ipc_recv` 时,环境将声明愿意去接收一个页映射。如果发送方发送一个页,那么那个页应该会被映射到接收者地址空间的 `dstva` 处。如果接收者在 `dstva` 已经有了一个页映射,那么已存在的那个页映射将被取消映射。
@ -540,31 +506,30 @@ JOS 用户异常栈也是一个页的大小,并且它的顶部被定义在虚
如果发送方和接收方都没有表示要转移这个页,那么就不会有页被转移。在任何 IPC 之后,内核将在接收方的 `Env` 结构上设置新的 `env_ipc_perm` 字段,以允许接收页,或者将它设置为 0表示不再接收。
###### 实现 IPC
##### 实现 IPC
```markdown
练习 15、实现 `kern/syscall.c` 中的 `sys_ipc_recv``sys_ipc_try_send`。在实现它们之前一起阅读它们的注释信息,因为它们要一起工作。当你在这些程序中调用 `envid2env` 时,你应该去设置 `checkperm` 的标志为 0这意味着允许任何环境去发送 IPC 消息到另外的环境,并且内核除了验证目标 envid 是否有效外,不做特别的权限检查。
> **练习 15**、实现 `kern/syscall.c` 中的 `sys_ipc_recv``sys_ipc_try_send`。在实现它们之前一起阅读它们的注释信息,因为它们要一起工作。当你在这些程序中调用 `envid2env` 时,你应该去设置 `checkperm` 的标志为 0这意味着允许任何环境去发送 IPC 消息到另外的环境,并且内核除了验证目标 envid 是否有效外,不做特别的权限检查。
接着实现 `lib/ipc.c` 中的 `ipc_recv``ipc_send` 函数。
> 接着实现 `lib/ipc.c` 中的 `ipc_recv``ipc_send` 函数。
使用 `user/pingpong``user/primes` 函数去测试你的 IPC 机制。`user/primes` 将为每个质数生成一个新环境,直到 JOS 耗尽环境为止。你可能会发现,阅读 `user/primes.c` 非常有趣,你将看到所有的 fork 和 IPC 都是在幕后进行。
```
> 使用 `user/pingpong``user/primes` 函数去测试你的 IPC 机制。`user/primes` 将为每个质数生成一个新环境,直到 JOS 耗尽环境为止。你可能会发现,阅读 `user/primes.c` 非常有趣,你将看到所有的 fork 和 IPC 都是在幕后进行。
```
小挑战!为什么 `ipc_send` 要循环调用?修改系统调用接口,让它不去循环。确保你能处理多个环境尝试同时发送消息到一个环境上的情况。
```
.
```markdown
小挑战!质数筛选是在大规模并发程序中传递消息的一个很巧妙的用法。阅读 C. A. R. Hoare 写的 《Communicating Sequential Processes》Communications of the ACM_ 21(8) (August 1978) 666-667并去实现矩阵乘法示例。
```
```markdown
小挑战控制消息传递的最令人印象深刻的一个例子是Doug McIlroy 的幂序列计算器,它在 [M. Douglas McIlroy《Squinting at Power Series》Software--Practice and Experience 20(7) (July 1990)661-683][4] 中做了详细描述。实现了它的幂序列计算器,并且计算了 _sin_ ( _x_ + _x_ ^3) 的幂序列。
```
> **小挑战!**为什么 `ipc_send` 要循环调用?修改系统调用接口,让它不去循环。确保你能处理多个环境尝试同时发送消息到一个环境上的情况。
```markdown
小挑战!通过应用 Liedtke 的论文([通过内核设计改善 IPC 性能][5])中的一些技术、或你可以想到的其它技巧,来让 JOS 的 IPC 机制更高效。为此,你可以随意修改内核的系统调用 API只要你的代码向后兼容我们的评级脚本就行。
```
.
> **小挑战!**质数筛选是在大规模并发程序中传递消息的一个很巧妙的用法。阅读 C. A. R. Hoare 写的 《Communicating Sequential Processes》Communications of the ACM_ 21(8) (August 1978) 666-667并去实现矩阵乘法示例。
.
> **小挑战!**控制消息传递的最令人印象深刻的一个例子是Doug McIlroy 的幂序列计算器,它在 [M. Douglas McIlroy《Squinting at Power Series》Software--Practice and Experience 20(7) (July 1990)661-683][4] 中做了详细描述。实现了它的幂序列计算器,并且计算了 _sin_ ( _x_ + _x_ ^3) 的幂序列。
.
> **小挑战!**通过应用 Liedtke 的论文([通过内核设计改善 IPC 性能][5])中的一些技术、或你可以想到的其它技巧,来让 JOS 的 IPC 机制更高效。为此,你可以随意修改内核的系统调用 API只要你的代码向后兼容我们的评级脚本就行。
**Part C 到此结束了。**确保你通过了所有的评级测试,并且不要忘了将你的小挑战的答案写入到 `answers-lab4.txt` 中。
@ -577,7 +542,7 @@ via: https://pdos.csail.mit.edu/6.828/2018/labs/lab4/
作者:[csail.mit][a]
选题:[lujun9972][b]
译者:[qhwdw](https://github.com/qhwdw)
校对:[校对者ID](https://github.com/校对者ID)
校对:[wxy](https://github.com/wxy)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
@ -587,4 +552,4 @@ via: https://pdos.csail.mit.edu/6.828/2018/labs/lab4/
[2]: https://pdos.csail.mit.edu/6.828/2018/labs/readings/i386/toc.htm
[3]: https://pdos.csail.mit.edu/6.828/2018/labs/readings/ia32/IA32-3A.pdf
[4]: https://swtch.com/~rsc/thread/squint.pdf
[5]: http://dl.acm.org/citation.cfm?id=168633
[5]: http://dl.acm.org/citation.cfm?id=168633

View File

@ -23,13 +23,13 @@
`cowsay` 是一个神奇的实用程序,它将文本作为 ASCII 艺术牛的讲话文本输出。
你可能会发现 `cowsey` 打包在你的默认存储库中,甚至可能已经安装了。对我来说,在 Fedora像这样安装:
你可能会发现 `cowsay` 打包在你的默认存储库中,甚至可能已经安装了。对我来说,在 Fedora像这样安装:
```
$ sudo dnf install -y cowsay
```
然后,用 `cowsey` 调用它,然后是你的消息。也许你想到昨天我们谈到的 [fortune 应用][1] 连接起来。
然后,用 `cowsay` 调用它,然后是你的消息。也许你想到昨天我们谈到的 [fortune 应用][1] 连接起来。
```
$ fortune | cowsay
@ -72,7 +72,7 @@ $ cowsay -f dragon "Run for cover, I feel a sneeze coming on."
我对 `cowsay` 的真正不满是,我今天没有足够的时间来为牛的挤奶 —— 一语双关。牛排价格太高了,我只是开个玩笑。
更严重的是,我已经完全忘记了 `cowsay` 直到我在学习 Ansible 的剧本时再次遇到它。如果你碰巧安装了 `cowyay`,当你运行Ansible 的剧本时,你会从一队奶牛那里获得输出。例如,运行这个剧本:
更严重的是,我已经完全忘记了 `cowsay` 直到我在学习 Ansible 的剧本时再次遇到它。如果你碰巧安装了 `cowsay`,当你运行 Ansible 的剧本时,你会从一队奶牛那里获得输出。例如,运行这个剧本:
```
- hosts:

View File

@ -1,3 +1,4 @@
Translating by ScarboroughCoral
How to turn on an LED with Fedora IoT
======

View File

@ -1,113 +0,0 @@
[#]: collector: (lujun9972)
[#]: translator: (qhwdw)
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )
[#]: subject: (How to get started in AI)
[#]: via: (https://opensource.com/article/18/12/how-get-started-ai)
[#]: author: (Gordon Haff https://opensource.com/users/ghaff)
How to get started in AI
======
Before you can begin working in artificial intelligence, you need to acquire some human intelligence.
![](https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/brain-think-ai-intelligence-ccby.png?itok=C-gK01E_)
I've both asked and been asked about the best way to learn more about artificial intelligence (AI). What should I read? What should I watch? I'll get to that. But, first, it's useful to break down this question, given that AI covers a lot of territory.
One important distinction to draw is between the research side of AI and the applied side. Cassie Kozyrkov of Google [drew this distinction][1] in a talk at the recent O'Reilly Artificial Intelligence Conference in London, and it's a good one.
Research AI is rather academic in nature and requires a heavy dose of math across a variety of disciplines before you even get to those parts that are specific to AI. This aspect of AI focuses on the algorithms and tools that drive the state of AI forward. For example, what neural network structures might improve vision recognition results? How might we make unsupervised learning a more generally useful approach? Can we find ways to understand better how deep learning pipelines come up with the answers they do?
Applied AI, on the other hand, is more about using existing tools to obtain useful results. Open source has played a big role here in providing free and often easy-to-use software in a variety of languages. Public cloud providers have also devoted a lot of attention to providing machine learning services, models, and datasets that make the onramp to getting started with AI much simpler than it would be otherwise.
I'll add at this point that applied AI practitioners shouldn't treat their tools as some sort of black box that spits out answers for mysterious reasons. At a minimum, they need to understand the limits and potential biases of different techniques, models, and data collection approaches. It's just that they don't necessarily need to delve deeply into all the theory underpinning every part of their toolchain.
Although it's probably less important for working in AI on a day-to-day basis, it's also useful to understand the broader context of AI. It goes beyond the narrow scope of deep learning on neural networks that have been so important to the gains made in reinforcement learning and supervised learning to date. For example, AI is often viewed as a way to augment (rather than replace) human judgment and decisions. But the handoff between machine and human has its own pitfalls.
With that background, here are some study areas and resources you may find useful.
### Research AI
In a lot of respects, a list of resources for research AI mirror those in an undergraduate (or even graduate) computer science program that's focused on AI. The main difference is that the syllabus you draw up may be more interdisciplinary than more traditionally focused university curricula.
Where you start will depend on your computer science and math background.
If it's minimal or rusty, but you still want to develop a deep understanding of AI fundamentals, you'll benefit from taking some math courses to start. There are many options on massive online open courses (MOOCs) like the nonprofit [edX][2] platform and [Coursera][3]. (Both platforms charge for certifications, but edX makes all the content available for free to people just auditing the course.)
Typical foundational courses could include:
+ [MIT's Calculus courses][22], starting with differentiation
+ [Linear Algebra][23] (University of Texas)
+ Probability and statistics, such as MIT's [Probability—The Science of Uncertainty and Data][24]
To get deeper into AI from a research perspective, you'll probably want to get into all these areas of mathematics and more. But the above should give you an idea of the general branches of study that are probably most important before delving into machine learning and AI proper.
In addition to MOOCs, resources such as [MIT OpenCourseWare][4] provide the syllabus and various supporting materials for a wide range of mathematics and computer science courses.
With the foundations in place, you can move onto more specialized courses in AI proper. Andrew Ng's AI MOOC, from when he was teaching at Stanford, was one of the early courses to popularize the whole online course space. Today, his [Neural Networks and Deep Learning][5] is part of the Deep Learning specialization at Coursera. There are corresponding programs on edX. For example, Columbia offers an [Artificial Intelligence MicroMasters][6].
In addition to courses, a variety of textbooks and other learning material are also available online. These include:
* [Neural Networks and Deep Learning][7]
* [Deep Learning][8] from MIT Press by Ian Goodfellow and Yoshua Bengio and Aaron Courville
### Applied AI
Applied AI is much more focused on using available tools than building new ones. Some appreciation of the mathematical underpinnings, especially statistics, is still useful—arguably even necessary—but you won't be majoring in that aspect of AI to the same degree you would in a research mode.
Programming is a core skill here. While different programming languages can come into play, a lot of libraries and toolsets—such as [PyTorch][9]—rely on Python, so that's a good skill to have. Especially if you have some level of programming background, MIT's [Introduction to Computer Science and Programming Using Python][10], based on its on-campus 6.001 course, is a good primer. If you're truly new to programming, Charles Severance's [Programming for Everybody (Getting Started with Python)][11] from the University of Michigan doesn't toss you into the deep end of the pool the way the MIT course does.
[The R programming language][12] is also a useful skill to add to your toolbox. While it's less used in machine learning (ML) per se, it's common for a variety of other data science tasks, and applied AI/ML and data science often blend in practice. For example, many tasks associated with organizing and cleaning data apply equally whatever analysis techniques you'll eventually use. A MOOC sequence like Harvard's [Data Science certificate][13] is an example of a set of courses that provide a good introduction to working with data.
Another open source software library you're likely to encounter if you do any work with AI is [TensorFlow][14]. It was originally developed by researchers and engineers from the Google Brain team within Google's AI organization. [Google offers a variety of tutorials][15] to get started with TensorFlow using the high-level Keras API. You can run TensorFlow locally as well as online in Google Cloud.
In general, all of the big public cloud providers offer online datasets and ML services that can be an easy way to get started. However, especially as you move beyond "play" datasets and applications, you need to start thinking seriously about the degree to which you want to be locked into a single provider.
Datasets for your exploratory learning projects are available from many different sources. In addition to the public cloud providers, [Kaggle][16] is another popular source and also a good learning resource more broadly. Government data is also increasingly available in digital form. The US Federal Government's [Data.gov][17] claims over 300,000 datasets. State and local governments also publish data on everything from restaurant health ratings to dogs' names.
### Miscellany
I'll close by noting that AI is a broad topic that isn't just about math, programming, and data. AI as a whole touches many other fields, including cognitive psychology, linguistics, game theory, operations research, and control systems. Indeed, a concern among at least some AI researchers today is that the field has become too fixated on a small number of techniques that have become powerful and interesting only quite recently because of the intersection of processing power and big data. Many longstanding problems in understanding how humans learn and reason remain largely unsolved. Developing at least some appreciation for these broader problem spaces will better enable you to place AI within a broader context.
One of my favorite examples is the [Humans and Autonomy Lab][18] at Duke. The work in this lab touches on all the challenges of humans working with machines, such as how autopilots can create ["Children of the Magenta"][19] who are unable to take control quickly if the automation fails. A basic brain-science course, such as MIT's [Introduction to Psychology][20], provides some useful context for the relationship between human intelligence and machine intelligence. Another course in a similar vein, but taught by the late Marvin Minsky from MIT's Electrical Engineering and Computer Science department, is [The Society of Mind][21].
If there's one key challenge to learning about AI, it's not that raw materials and tools aren't readily available. It's that there are so many of them. My objective hasn't been to give you a comprehensive set of pointers. Rather, it's been to both point out the different paths you can take and provide you with some possible starting points. Happy learning!
--------------------------------------------------------------------------------
via: https://opensource.com/article/18/12/how-get-started-ai
作者:[Gordon Haff][a]
选题:[lujun9972][b]
译者:[译者ID](https://github.com/译者ID)
校对:[校对者ID](https://github.com/校对者ID)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
[a]: https://opensource.com/users/ghaff
[b]: https://github.com/lujun9972
[1]: https://www.youtube.com/watch?v=RLtI7r3QUyY
[2]: https://www.edx.org/
[3]: https://www.coursera.org/
[4]: https://ocw.mit.edu/index.htm
[5]: https://www.coursera.org/learn/neural-networks-deep-learning
[6]: https://www.edx.org/micromasters/columbiax-artificial-intelligence
[7]: http://neuralnetworksanddeeplearning.com/
[8]: http://www.deeplearningbook.org/
[9]: https://pytorch.org/
[10]: https://www.edx.org/course/introduction-to-computer-science-and-programming-using-python
[11]: https://www.coursera.org/learn/python
[12]: https://www.r-project.org/about.html
[13]: https://www.edx.org/professional-certificate/harvardx-data-science
[14]: https://www.tensorflow.org/
[15]: https://www.tensorflow.org/tutorials/
[16]: https://www.kaggle.com/
[17]: https://www.data.gov/
[18]: https://hal.pratt.duke.edu/
[19]: https://99percentinvisible.org/episode/children-of-the-magenta-automation-paradox-pt-1/
[20]: https://ocw.mit.edu/courses/brain-and-cognitive-sciences/9-00sc-introduction-to-psychology-fall-2011/
[21]: https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-868j-the-society-of-mind-fall-2011/
[22]: https://www.edx.org/course/calculus-1a-differentiation
[23]: https://www.edx.org/course/linear-algebra-foundations-to-frontiers
[24]: https://courses.edx.org/courses/course-v1:MITx+6.431x+3T2018/course/

View File

@ -189,7 +189,7 @@ export -n robots
### 接下来
了解过环境变量的知识之后,你已经到达了可能对自己和他人造成危险的水平,接下来就需要了解如何通过使用 `_aliases` 来让环境变得更安全、更友好以保护自己了。
了解过环境变量的知识之后,你已经到达了可能对自己和他人造成危险的水平,接下来就需要了解如何通过使用别名来让环境变得更安全、更友好以保护自己了。
--------------------------------------------------------------------------------

View File

@ -1,5 +1,5 @@
[#]: collector: (lujun9972)
[#]: translator: (dianbanjiu )
[#]: translator: ( dianbanjiu )
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )
@ -7,38 +7,38 @@
[#]: via: (https://www.2daygeek.com/check-laptop-battery-status-and-charging-state-in-linux-terminal/)
[#]: author: (Magesh Maruthamuthu https://www.2daygeek.com/author/magesh/)
5 Ways To Check Laptop Battery Status And Level From Linux Terminal
从 Linux 终端查看笔记本电池状态等级的 5 个方法
======
We can easily check the battery status through GUI such as current battery percentage, whether its charging or not charging and how long it will be usable without charging, but we cant able to check the battery health and other related information.
我们可以轻松地通过图形化界面查看当前电量百分比、是否在充电以及当前电量还可以使用多长时间等电池状态,但是却无法查看电池健康度等相关信息。
In this scenario what will be the solutions.
在这篇文章就是为了解决这些问题。
Yes, we have few utilities available for this in Linux and it can be achieved through command line.
在 Linux 上有很多实用工具可以用命令行进行使用。
We are going to discuss about this topic today through this article and i will try to cover possible information i can.
这篇文章今天就要探讨这个主题,我会尽我所能的覆盖尽可能多的信息。
Checking your battery health monthly once is something good. It will help you to identify whether we are facing any battery or charge related issues.
每月检查一次你的电池健康度是一个很好的想法。它可以帮你检查你当前遇到的问题是否与电池或者充电相关。
Also, we can see battery model name, power source, vendor and battery technology, etc,.
同时,我们也可以查看电池模组名称、电源、出售商以及电池规格等。
Power management is a feature that turns off the power or switches systems components to a low-power state when inactive.
电源管理是在非活跃状态时关闭电源或者切换系统的组件到低耗模式的一种功能。
### Following Utilities are available in Linux to Check Battery Status.
### 几种在 Linux 下检查电池状态的实用工具
* `upower`: upower is a command line tool which provides an interface to enumerate power sources on the system.
* `acpi`: acpi Shows information from the /proc or the /sys filesystem, such as battery status or thermal information.
* `batstat`: batstat is a command line tool to print battery status for linux.
* `tlp`: TLP brings you the benefits of advanced power management for Linux without changing any configuration.
* `class file`: The sysfs filesystem is a pseudo-filesystem which provides an interface to kernel data structures.
* `upower`: upower 是一个提供了接口来罗列系统中电源的命令行工具。
* `acpi`: acpi显示来自 /proc 或者 /sys 文件中的一些信息,例如电池状态或者热量信息
* `batstat`: batstat 是一个为 Linux 打印电池状态的命令行工具。
* `tlp`: TLP 可以为你带来更高级的电源管理,而无需修改任何配置。
* `class file`: 这个文件系统是一个提供了内核数据结构接口的伪文件系统。
### How to Check Laptop Battery Status Using upower Command?
### 如何使用 upower 命令检查笔记本电池状态
[upower][1] is a command line tool that provides an interface to enumerate power sources on the system. It control the latency of different operations on your computer, which enables you to save significant amounts of power.
[upower][1] 是一个提供了接口来罗列系统中电源的命令行工具。它在你的电脑上可以控制不同操作的延迟,这可以为你节省很大一部分电量。
Just run the following command to get the battery and its related information on Linux.
只需要在 Linux 中运行以下命令获取电池以及它所依赖的其他信息。
```
$ upower -i /org/freedesktop/UPower/devices/battery_BAT0
@ -72,7 +72,7 @@ $ upower -i /org/freedesktop/UPower/devices/battery_BAT0
1543847178 10.714 discharging
```
To check the specific information about battery, use the following format.
使用下面的格式检查电池的特定信息。
```
$ upower -i /org/freedesktop/UPower/devices/battery_BAT0 | grep -i "state\|percentage\|time to empty"
@ -81,7 +81,7 @@ $ upower -i /org/freedesktop/UPower/devices/battery_BAT0 | grep -i "state\|perce
percentage: 43%
```
Its same as above, but its taken after power cable plugged in, thats why the state showing charging.
这个类似于上面的那个,但是充电线缆的插入就相当于一个令牌,这也就是为什么下面会显示正在充电状态的原因。
```
$ upower -i /org/freedesktop/UPower/devices/battery_BAT0 | grep -i "state\|percentage\|time to empty"
@ -89,15 +89,15 @@ $ upower -i /org/freedesktop/UPower/devices/battery_BAT0 | grep -i "state\|perce
percentage: 41%
```
### How to Check Laptop Battery Status Using TLP Command?
### 如何使用 TLP 命令检查笔记本电池状态
TLP is a free opensource feature-rich command line tool which optimize laptop battery without making any configuration change.
TLP 是一个自由开源多功能的命令行工具,它可以优化笔记本电池而无需修改任何配置。
TLP brings you the benefits of advanced power management for Linux without the need to understand every technical detail. TLP comes with a default configuration already optimized for battery life, so you may just install and forget it. Nevertheless TLP is highly customizable to fulfil your specific requirements.
TLP 可以为你的 Linux 带来更高级的电源管理而无需理解任何技术细节。TLP 默认附带了一个已经为你的电池优化好的配置,所以你可以安装好后就不再管它了。尽管 TLP 是一个可以根据你的需求高度可定制的工具。
TLP package is available in most of the Linux distribution official repository such as Arch, Debian, Fedora, Gentoo, openSUSE, etc. Use your distribution Package Manager to install the TLP utility.
TLP 在绝大多数 Linux 发行版,例如 Arch、Debian、Fedora、Gentoo、openSUSE等的官方库中都可用。使用你的 Linux 发行版的包管理安装 TLP 即可。
Just run the following command to get the battery and its related information on Linux.
只需要在 Linux 中运行以下命令获取电池以及其他所依赖的信息。
```
$ sudo tlp-stat -b
@ -117,7 +117,7 @@ Charge = 42.0 [%]
Capacity = 87.1 [%]
```
To see other information as well.
也可以查看其他的信息。
```
$ sudo tlp-stat -s
@ -139,16 +139,16 @@ Mode = battery
Power source = battery
```
### How to Check Laptop Battery Status Using ACPI Command?
### 如何使用 ACPI 命令检查电池状态
ACPI stands for Advanced Configuration and Power Interface modules are kernel modules for different ACPI parts. They enable special ACPI functions or add information to /proc or /sys. These information can be parsed by acpid for events or other monitoring applications.
ACPI 代表高级配置和电源接口模块,它们是不同 ACPI 部件的内核模块。它们启用特殊的 ACPI 函数向 /proc 或者 /sys 中添加信息。这些信息可以通过事件或者其他监控程序的 acpid 进行解析。
```
$ acpi
Battery 0: Charging, 43%, 01:05:11 until charged
```
To see battery capacity.
查看电池容量。
```
$ acpi -i
@ -156,7 +156,7 @@ Battery 0: Charging, 43%, 01:05:07 until charged
Battery 0: design capacity 3817 mAh, last full capacity 3324 mAh = 87%
```
To see more details about battery and related information.
查看更多有关电池及其相关的信息。
```
$ acpi -V
@ -177,9 +177,9 @@ Cooling 10: x86_pkg_temp no state information available
Cooling 11: Processor 0 of 10
```
### How to Check Laptop Battery Status Using Batstat Command?
### 如何使用 Batstat 命令查看笔记本电池状态
batstat is a command line tool to print battery status in linux terminal.
batstat 是一个在 Linux 终端打印电池信息的命令行工具。
```
Status: Charging
@ -193,13 +193,13 @@ Time elapsed: 0: 0:12 since 49.00%
0: 0: 0 49.00%
```
### How to Check Laptop Battery Status Using sysfs filesystem?
### 如何使用 sysfs 文件系统查看笔记本电池状态
The sysfs filesystem is a pseudo-filesystem which provides an interface to kernel data structures. The files under sysfs provide information about devices, kernel modules, filesystems, and other kernel components.
sysfs 文件系统是一个提供了内核数据结构接口的伪文件系统。sysfs 下的文件提供有关设备、内核模块、文件系统和其他内核组件的信息。
The sysfs filesystem is commonly mounted at /sys. Typically, it is mounted automatically by the system, but it can also be mounted manually using a command such as `mount -t sysfs sysfs /sys`
sysfs 文件系统通常挂载在 /sys。通常来说它会被系统自动挂载但是也可以使用例如 `mount -t sysfs sysfs /sys` 命令进行手动挂载。
Many of the files in the sysfs filesystem are read-only, but some files are writable, allowing kernel variables to be changed. To avoid redundancy, symbolic links are heavily used to connect entries across the filesystem tree.
在 sysfs 文件系统中的很多文件都是只读的,但也有一些是可写的,允许更改内核变量。为了避免冗余,符号链接被大量用于连接文件系统数中的条目。
```
$ cat /sys/class/power_supply/BAT0/*
@ -247,7 +247,7 @@ via: https://www.2daygeek.com/check-laptop-battery-status-and-charging-state-in-
作者:[Magesh Maruthamuthu][a]
选题:[lujun9972][b]
译者:[译者ID](https://github.com/译者ID)
译者:[dianbanjiu](https://github.com/dianbanjiu)
校对:[校对者ID](https://github.com/校对者ID)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出

View File

@ -0,0 +1,113 @@
[#]: collector: (lujun9972)
[#]: translator: (qhwdw)
[#]: reviewer: ()
[#]: publisher: ()
[#]: url: ()
[#]: subject: (How to get started in AI)
[#]: via: (https://opensource.com/article/18/12/how-get-started-ai)
[#]: author: (Gordon Haff https://opensource.com/users/ghaff)
学习人工智能如何起步
======
在你开始从事人工智能之前,你需要先了解人类的智能。
![](https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/brain-think-ai-intelligence-ccby.png?itok=C-gK01E_)
我曾经问过别人也被别人问过关于学习人工智能最好的方式是什么?我应该去阅读什么?我应该去关注什么?后面我将讲到这些,但是,考虑到人工智能涉及很多领域,我把这个问题分开来讲可能更好理解。
学习人工智能很重要的一点是区别开研究方面和应用方面。Google 的 Cassie Kozyrkov 在近日于伦敦举行的 OReilly 人工智能会议的一个演讲中 [描述了这个区别][1],并且这是一个很好的区别。
研究人工智能在本质上是学术性的,在你能够获得人工智能的某些细节之前,需要大量的跨各类学科的数学知识。这部分人工智能关注于算法和驱动人工智能发展的工具。比如,什么样的神经网络结构能够改善视觉识别的结果?我们如何使无监督学习成为更有用的方法?我们能否找到一个更好的方法,去理解深度学习流水线是如何得出答案的?
另一方面,人工智能应用更多是关于使用现有工具去获取有用的结果。开源在这里发挥了一个重要的作用,那就是免费提供了易于使用的、各种语言的软件。公有云提供商也致力于提供大量的机器学习、模型、以及数据集,这使得人工智能入门比其它的要简单的多。
在这个问题上我想补充一点,那就是人工智能的从业者不应该为了故弄玄虚而将它们的工具搞成只输出答案的黑匣子。至少,他们应该去了解不同技术、模型、和数据采集方法的限制和潜在偏差。但不需要去深入研究他们工具链中每个部分的理论。
虽然在日常工作中人工智能可能并不那么重要,但理解人工智能的大量的背景知识还是很有用的。人工智能已经超越了神经网络上深度学习的狭窄范围,目前神经网络上的强化学习和监督学习已经取得重要成就。例如,人工智能经常被视为是增强(而不是替代)人类判断和决策的一种方法。但是在机器和人类之间切换还有它自己的缺陷。
有了这些背景知识,下面是的一些研究领域和资源,你可能发现会很有用。
### 研究人工智能
在很多方面,用于人工智能研究的一个资源清单,可以反映出本科(甚至是研究生)的计算机科学项目都是专注于人工智能。最主要的区别是,你起草的教学大纲比起传统的大纲更关注于跨学科。
你的计算机科学和数学背景知识决定了你的起点。
如果你的计算机科学和数据背景知识很差或已经荒芜了但你还希望能够深入了解人工智能的基本原理那么从一些数学课程开始将会让你受益。MOOCs 上像非盈利的 [edX][2] 平台和 [Coursera][3] 上都有许多可供你选择的课程(这两个平台都对认证收费,但 edX 上所有的课程,对旁听者是全免费的)。
典型的基础课程包括:
+ [MIT 的微积分课程][22],从微分开始学习
+ [线性代数][23] (德克萨斯大学)
+ 概率与统计,比如 MIT 的 [概率 — 不确定性与数据科学][24]
从一个研究的角度去深入人工智能,你可能需要深入所有的这些数据领域,甚至更多。在深入研究机器学习和人工智能之前,上述的内容应该会让你得到一些常见研究分支的大致概念。
除了 MOOCs 之外,像 [MIT OpenCourseWare][4] 这样的资源也提供了大量的数学和计算机科学课程的大纲和各种支持材料。
有了这些基础你就可以学习更专业的人工智能课程了。Andrew Ng 在斯坦福大学教的 “AI MOOC” 就是整个在线课程中最早流行起来的课程之一。今天,他的 [神经网络和深度学习][5] 也是 Coursera 深度学习专业的一部分。在 edX 上也有相关的一些项目,比如,哥伦比亚大学提供的一个 [人工智能 MicroMasters][6]。
除了课程之外,也可以在网上找到各种范例和其它学习材料。这些包括:
* [神经网络和深度学习][7]
* MIT 的 Ian Goodfellow、Yoshua Bengio、Aaron Courville 出版的 [深度学习][8]
### 应用人工智能
人工智能应用更关注于使用可用的工具,而不是去构建新工具。对一些底层的数学,尤其是统计学的了解仍然是非常有用的 — 甚至可以说是必需的 — 但对这些知识的了解程度不像研究人工智能的要求那么高。
在这里编程是核心技能。虽然可以使用不同的编程语言去做,但是一些库和工具集 — 比如 Python 的 [PyTorch][9]在这方面有很好的专长。尤其是如果你有一些编程方面的背景知识MIT 的 [计算机科学入门和使用 Python 编程][10],它是基于 MIT 的 6.001 课程,是一个非常好的启蒙课程。如果你编程零基础,来自密歇根大学的 Charles Severance 的 [人人学编程Python 使用入门)][11] 是个很好的开端,它不会像 MIT 的课程那样,把你一下子扔进代码的汪洋大海。
[R 编程语言][12] 也是一个应该增加到你的技能库中的很有用的技能。虽然它在机器学习ML中使用的很少但它在其它数据科学任务中很常见并且经常与人工智能/机器学习和数据科学的应用实践结合在一起。比如,许多使用原始和数据清洗相关的应用任务,此外还有你最终要使用的诸如此类的分析技术,都将使用到它。一个 MOOC 系列,像 Harvard 的 [数据科学认证][13] 就是一整套课程的一个例子,这些课程介绍了如何去很好地处理数据。
如果你从事人工智能方面的工作,那么你很可能会遇到的另一个开源软件库就是 [TensorFlow][14]。它最初是由 Google 人工智能团队中的 Google 智慧团队的研发工程师开发的。[Google 提供了许多教程][15] 让你通过高级 Keras API 去开始使用 TensorFlow。你既可以在 Google 云上也可以在本地运行 TensorFlow。
通常,大的公有云提供商都提供在线数据集和易于使用的机器学习服务。但是,在你开始去 “玩” 数据集和应用之前,你需要考虑清楚,一旦开始选定一个提供商,你将被它们 “锁定” 的程度。
你的探索学习项目所需的数据集可以从许多不同的源获得。除了公有云提供商之外,[Kaggle][16] 是另一个受欢迎的源,总体来看,它也是一个比较好的学习源。以数字形式提供的政府数据也越来越多了。美国联邦政府的 [Data.gov][17] 声称它提供超过 300,000 的数据集。州和地方政府也发布从餐馆健康评级到狗的名字的所有数据。
### 研究和应用人工智能兼而有之
最后我想说明的一点是,人工智能不仅是与数学、编程、和数据有关的一个宽泛的主题。人工智能作为一个综合体涉及到了许多其它的领域,包括心理学、语言学、博弈论、运筹学和控制系统。确实,现在有一些人工智能研究者担心,由于处理能力和大数据的结合,使得该领域过于关注最近才变得强大和有趣的少数几个技术。在了解人类如何学习和推理方面,许多长期存在的问题仍未解决。不管怎样,对这些广泛存在的问题有一个了解,将更好地让你在更广泛的背景中评估人工智能。
我比较喜欢的其中一个示例是杜克大学的 [人类和自治实验室][18]。这个实验室的工作涉及人类与机器工作所面临的全部挑战,比如,如果自动化设备失效,自动驾驶仪如何设计才能让那些[“洋红色的孩子“][19] 快速取得对飞机的控制。一个基础的大脑科学课程,比如 MIT 的 [心理学导论][20]它提供了关于人类智能和机器智能之间关系的一些很有用的内容。另一个类似的课程是MIT 电子工程与计算机科学系已故教授 Marvin Minsky 的 [心灵的社会][21]。
关于学习人工智能,假如说有一个最重要的挑战,那它不是原材料和工具不易获得,而是它们只有这么多。我的目标并不是给你一个全面的指导,相反,而是指出了你可以去学习的不同路径,以及为你提供一些可能的起点。祝你学习愉快!
--------------------------------------------------------------------------------
via: https://opensource.com/article/18/12/how-get-started-ai
作者:[Gordon Haff][a]
选题:[lujun9972][b]
译者:[qhwdw](https://github.com/qhwdw)
校对:[校对者ID](https://github.com/校对者ID)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
[a]: https://opensource.com/users/ghaff
[b]: https://github.com/lujun9972
[1]: https://www.youtube.com/watch?v=RLtI7r3QUyY
[2]: https://www.edx.org/
[3]: https://www.coursera.org/
[4]: https://ocw.mit.edu/index.htm
[5]: https://www.coursera.org/learn/neural-networks-deep-learning
[6]: https://www.edx.org/micromasters/columbiax-artificial-intelligence
[7]: http://neuralnetworksanddeeplearning.com/
[8]: http://www.deeplearningbook.org/
[9]: https://pytorch.org/
[10]: https://www.edx.org/course/introduction-to-computer-science-and-programming-using-python
[11]: https://www.coursera.org/learn/python
[12]: https://www.r-project.org/about.html
[13]: https://www.edx.org/professional-certificate/harvardx-data-science
[14]: https://www.tensorflow.org/
[15]: https://www.tensorflow.org/tutorials/
[16]: https://www.kaggle.com/
[17]: https://www.data.gov/
[18]: https://hal.pratt.duke.edu/
[19]: https://99percentinvisible.org/episode/children-of-the-magenta-automation-paradox-pt-1/
[20]: https://ocw.mit.edu/courses/brain-and-cognitive-sciences/9-00sc-introduction-to-psychology-fall-2011/
[21]: https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-868j-the-society-of-mind-fall-2011/
[22]: https://www.edx.org/course/calculus-1a-differentiation
[23]: https://www.edx.org/course/linear-algebra-foundations-to-frontiers
[24]: https://courses.edx.org/courses/course-v1:MITx+6.431x+3T2018/course/