Caffeinated 6.828：实验 4：抢占式多任务处理

Summary: 在本实验中，你将在多个同时活动的用户模式环境之间实现抢占式多任务处理。

在本实验中，你将在多个同时活动的用户模式环境之间实现抢占式多任务处理。

![](/data/attachment/album/201812/16/125727xit7uuvtt0r7j715.jpg)

### 简介

在本实验中，你将在多个同时活动的用户模式环境之间实现抢占式多任务处理。

在 Part A 中，你将在 JOS 中添加对多处理器的支持，以实现循环调度。并且添加基本的环境管理方面的系统调用（创建和销毁环境的系统调用、以及分配/映射内存）。

在 Part B 中，你将要实现一个类 Unix 的 `fork()`，它将允许一个用户模式中的环境去创建一个它自已的副本。

最后，在 Part C 中，你将在 JOS 中添加对进程间通讯（IPC）的支持，以允许不同用户模式环境之间进行显式通讯和同步。你也将要去添加对硬件时钟中断和优先权的支持。

#### 预备知识

使用 git 去提交你的实验 3 的源代码，并获取课程仓库的最新版本，然后创建一个名为 `lab4` 的本地分支，它跟踪我们的名为 `origin/lab4` 的远程 `lab4` 分支：

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

实验 4 包含了一些新的源文件，在开始之前你应该去浏览一遍：

```
kern/cpu.h       Kernel-private definitions for multiprocessor support
kern/mpconfig.c  Code to read the multiprocessor configuration
kern/lapic.c     Kernel code driving the local APIC unit in each processor
kern/mpentry.S   Assembly-language entry code for non-boot CPUs
kern/spinlock.h  Kernel-private definitions for spin locks, including the big kernel lock
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。我们计划为每个部分分配一周的时间。

和以前一样，你需要完成实验中出现的、所有常规练习和至少一个挑战问题。（不是每个部分做一个挑战问题，是整个实验做一个挑战问题即可。）另外，你还要写出你实现的挑战问题的详细描述。如果你实现了多个挑战问题，你只需写出其中一个即可，虽然我们的课程欢迎你完成越多的挑战越好。在动手实验之前，请将你的挑战问题的答案写在一个名为 `answers-lab4.txt` 的文件中，并把它放在你的 `lab` 目录的根下。

### Part A：多处理器支持和协调多任务处理

在本实验的第一部分，将去扩展你的 JOS 内核，以便于它能够在一个多处理器的系统上运行，并且要在 JOS 内核中实现一些新的系统调用，以便于它允许用户级环境创建附加的新环境。你也要去实现协调的循环调度，在当前的环境自愿放弃 CPU（或退出）时，允许内核将一个环境切换到另一个环境。稍后在 Part C 中，你将要实现抢占调度，它允许内核在环境占有 CPU 一段时间后，从这个环境上重新取回对 CPU 的控制，那怕是在那个环境不配合的情况下。

#### 多处理器支持

我们继续去让 JOS 支持 “对称多处理器”（SMP），在一个多处理器的模型中，所有 CPU 们都有平等访问系统资源（如内存和 I/O 总线）的权力。虽然在 SMP 中所有 CPU 们都有相同的功能，但是在引导进程的过程中，它们被分成两种类型：引导程序处理器（BSP）负责初始化系统和引导操作系统；而在操作系统启动并正常运行后，应用程序处理器（AP）将被 BSP 激活。哪个处理器做 BSP 是由硬件和 BIOS 来决定的。到目前为止，你所有的已存在的 JOS 代码都是运行在 BSP 上的。

在一个 SMP 系统上，每个 CPU 都伴有一个本地 APIC（LAPIC）单元。这个 LAPIC 单元负责传递系统中的中断。LAPIC 还为它所连接的 CPU 提供一个唯一的标识符。在本实验中，我们将使用 LAPIC 单元（它在 `kern/lapic.c` 中）中的下列基本功能：

* 读取 LAPIC 标识符（APIC ID），去告诉那个 CPU 现在我们的代码正在它上面运行（查看 `cpunum()`）。
* 从 BSP 到 AP 之间发送处理器间中断（IPI） `STARTUP`，以启动其它 CPU（查看 `lapic_startap()`）。
* 在 Part C 中，我们设置 LAPIC 的内置定时器去触发时钟中断，以便于支持抢占式多任务处理（查看 `apic_init()`）。

一个处理器使用内存映射的 I/O（MMIO）来访问它的 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` 的测试运行之前，你还要做下一个练习。
>
>
>

##### 引导应用程序处理器

在引导应用程序处理器之前，引导程序处理器应该会首先去收集关于多处理器系统的信息，比如总的 CPU 数、它们的 APIC ID 以及 LAPIC 单元的 MMIO 地址。在 `kern/mpconfig.c` 中的 `mp_init()` 函数，通过读取内存中位于 BIOS 区域里的 MP 配置表来获得这些信息。

`boot_aps()` 函数（在 `kern/init.c` 中）驱动 AP 的引导过程。AP 们在实模式中开始，与 `boot/boot.S` 中启动引导加载程序非常相似。因此，`boot_aps()` 将 AP 入口代码（`kern/mpentry.S`）复制到实模式中的那个可寻址内存地址上。不像使用引导加载程序那样，我们可以控制 AP 将从哪里开始运行代码；我们复制入口代码到 `0x7000`（`MPENTRY_PADDR`）处，但是复制到任何低于 640KB 的、未使用的、页对齐的物理地址上都是可以运行的。

在那之后，通过发送 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` 字段中。

>
> **练习 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()` 上测试失败，我们在后面会修复它）。
>
>
>

.

>
> **问题 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 的状态对其它处理器是不公开的，而全局状态是整个系统共享的。`kern/cpu.h` 定义了大部分每个 CPU 的状态，包括 `struct CpuInfo`，它保存了每个 CPU 的变量。`cpunum()` 总是返回调用它的那个 CPU 的 ID，它可以被用作是数组的索引，比如 `cpus`。或者，宏 `thiscpu` 是当前 CPU 的 `struct CpuInfo` 缩略表示。

下面是你应该知道的每个 CPU 的状态：

* **每个 CPU 的内核栈**

因为内核能够同时捕获多个 CPU，因此，我们需要为每个 CPU 准备一个单独的内核栈，以防止它们运行的程序之间产生相互干扰。数组 `percpu_kstacks[NCPU][KSTKSIZE]` 为 NCPU 的内核栈资产保留了空间。

在实验 2 中，你映射的 `bootstack` 所引用的物理内存，就作为 `KSTACKTOP` 以下的 BSP 的内核栈。同样，在本实验中，你将每个 CPU 的内核栈映射到这个区域，而使用保护页做为它们之间的缓冲区。CPU 0 的栈将从 `KSTACKTOP` 处向下增长；CPU 1 的栈将从 CPU 0 的栈底部的 `KSTKGAP` 字节处开始，依次类推。在 `inc/memlayout.h` 中展示了这个映射布局。
* **每个 CPU 的 TSS 和 TSS 描述符**

为了指定每个 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）上当前正在运行的环境上。
* **每个 CPU 的系统寄存器**

所有的寄存器，包括系统寄存器，都是一个 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，你应该看到类似下面的输出：

```
    ...
    Physical memory: 66556K available, base = 640K, extended = 65532K
    check_page_alloc() succeeded!
    check_page() succeeded!
    check_kern_pgdir() succeeded!
    check_page_installed_pgdir() succeeded!
    SMP: CPU 0 found 4 CPU(s)
    enabled interrupts: 1 2
    SMP: CPU 1 starting
    SMP: CPU 2 starting
    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()` 中，在切换到用户模式之前释放锁。不能太早也不能太晚，否则你将可能会产生争用或死锁。

>
> **练习 5**、在上面所描述的情况中，通过在合适的位置调用 `lock_kernel()` 和 `unlock_kernel()` 应用大内核锁。
>
>
> 如果你的锁定是正确的，如何去测试它？实际上，到目前为止，还无法测试！但是在下一个练习中，你实现了调度之后，就可以测试了。
>
>
>

.

>
> **问题 2**、看上去使用一个大内核锁，可以保证在一个时间中只有一个 CPU 能够运行内核代码。为什么每个 CPU 仍然需要单独的内核栈？描述一下使用一个共享内核栈出现错误的场景，即便是在它使用了大内核锁保护的情况下。
>
>
> **小挑战！**大内核锁很简单，也易于使用。尽管如此，它消除了内核模式的所有并发。大多数现代操作系统使用不同的锁，一种称之为细粒度锁定的方法，去保护它们的共享的栈的不同部分。细粒度锁能够大幅提升性能，但是实现起来更困难并且易出错。如果你有足够的勇气，在 JOS 中删除大内核锁，去拥抱并发吧！
>
>
> 由你来决定锁的粒度（一个锁保护的数据量）。给你一个提示，你可以考虑在 JOS 内核中使用一个自旋锁去确保你独占访问这些共享的组件：
>
>
> * 页分配器
> * 控制台驱动
> * 调度器
> * 你将在 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 的控制禅让给另外的一个环境。

>
> **练习 6**、像上面描述的那样，在 `sched_yield()` 中实现循环调度。不要忘了去修改 `syscall()` 以派发 `sys_yield()`。
>
>
> 确保在 `mp_main` 中调用了 `sched_yield()`。
>
>
> 修改 `kern/init.c` 去创建三个（或更多个！）运行程序 `user/yield.c`的环境。
>
>
> 运行 `make qemu`。在它终止之前，你应该会看到像下面这样，在环境之间来回切换了五次。
>
>
> 也可以使用几个 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 内核监视器。如果它什么也没有发生，那么你应该在继续之前修复你的代码。
>
>
> **问题 3**、在你实现的 `env_run()` 中，你应该会调用 `lcr3()`。在调用 `lcr3()` 的之前和之后，你的代码引用（至少它应该会）变量 `e`，它是 `env_run` 的参数。在加载 `%cr3` 寄存器时，MMU 使用的地址上下文将马上被改变。但一个虚拟地址（即 `e`）相对一个给定的地址上下文是有意义的 —— 地址上下文指定了物理地址到那个虚拟地址的映射。为什么指针 `e` 在地址切换之前和之后被解除引用？
>
>
>

.

>
> **问题 4**、无论何时，内核从一个环境切换到另一个环境，它必须要确保旧环境的寄存器内容已经被保存，以便于它们稍后能够正确地还原。为什么？这种事件发生在什么地方？
>
>
>

.

>
> 小挑战！给内核添加一个小小的调度策略，比如一个固定优先级的调度器，它将会给每个环境分配一个优先级，并且在执行中，较高优先级的环境总是比低优先级的环境优先被选定。如果你想去冒险一下，尝试实现一个类 Unix 的、优先级可调整的调度器，或者甚至是一个彩票调度器或跨步调度器。（可以在 Google 中查找“彩票调度”和“跨步调度”的相关资料）
>
>
> 写一个或两个测试程序，去测试你的调度算法是否工作正常（即，正确的算法能够按正确的次序运行）。如果你实现了本实验的 Part B 和 Part C 部分的 `fork()` 和 IPC，写这些测试程序可能会更容易。
>
>
>

.

>
> 小挑战！目前的 JOS 内核还不能应用到使用了 x87 协处理器、MMX 指令集、或流式 SIMD 扩展（SSE）的 x86 处理器上。扩展数据结构 `Env` 去提供一个能够保存处理器的浮点状态的地方，并且扩展上下文切换代码，当从一个环境切换到另一个环境时，能够保存和还原正确的状态。`FXSAVE` 和 `FXRSTOR` 指令或许对你有帮助，但是需要注意的是，这些指令在旧的 x86 用户手册上没有，因为它是在较新的处理器上引入的。写一个用户级的测试程序，让它使用浮点做一些很酷的事情。
>
>
>

#### 创建环境的系统调用

虽然你的内核现在已经有了在多个用户级环境之间切换的功能，但是由于内核初始化设置的原因，它在运行环境时仍然是受限的。现在，你需要去实现必需的 JOS 系统调用，以允许用户环境去创建和启动其它的新用户环境。

Unix 提供了 `fork()` 系统调用作为它的进程创建原语。Unix 的 `fork()` 通过复制调用进程（父进程）的整个地址空间去创建一个新进程（子进程）。从用户空间中能够观察到它们之间的仅有的两个差别是，它们的进程 ID 和父进程 ID（由 `getpid` 和 `getppid` 返回）。在父进程中，`fork()` 返回子进程 ID，而在子进程中，`fork()` 返回 0。默认情况下，每个进程得到它自己的私有地址空间，一个进程对内存的修改对另一个进程都是不可见的。

为创建一个用户模式下的新的环境，你将要提供一个不同的、更原始的 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`：

在一个给定的环境中，取消映射一个给定的已映射的虚拟地址。

上面所有的系统调用都接受环境 ID 作为参数，JOS 内核支持一个约定，那就是用值 “0” 来表示“当前环境”。这个约定在 `kern/env.c` 中的 `envid2env()` 中实现的。

在我们的 `user/dumbfork.c` 中的测试程序里，提供了一个类 Unix 的 `fork()` 的非常原始的实现。这个测试程序使用了上面的系统调用，去创建和运行一个复制了它自己地址空间的子环境。然后，这两个环境像前面的练习那样使用 `sys_yield` 来回切换，父进程在迭代 10 次后退出，而子进程在迭代 20 次后退出。

>
> **练习 7**、在 `kern/syscall.c` 中实现上面描述的系统调用，并确保 `syscall()` 能调用它们。你将需要使用 `kern/pmap.c` 和 `kern/env.c` 中的多个函数，尤其是要用到 `envid2env()`。目前，每当你调用 `envid2env()` 时，在 `checkperm` 中传递参数 1。你务必要做检查任何无效的系统调用参数，在那个案例中，就返回了 `-E_INVAL`。使用 `user/dumbfork` 测试你的 JOS 内核，并在继续之前确保它运行正常。
>
>
>

.

>
> **小挑战！**添加另外的系统调用，必须能够读取已存在的、所有的、环境的重要状态，以及设置它们。然后实现一个能够 fork 出子环境的用户模式程序，运行它一小会（即，迭代几次 `sys_yield()`），然后取得几张屏幕截图或子环境的检查点，然后运行子环境一段时间，然后还原子环境到检查点时的状态，然后从这里继续开始。这样，你就可以有效地从一个中间状态“回放”了子环境的运行。确保子环境与用户使用 `sys_cgetc()` 或 `readline()` 执行了一些交互，这样，那个用户就能够查看和突变它的内部状态，并且你可以通过给子环境给定一个选择性遗忘的状况，来验证你的检查点/重启动的有效性，使它“遗忘”了在某些点之前发生的事情。
>
>
>

到此为止，已经完成了本实验的 Part A 部分；在你运行 `make grade` 之前确保它通过了所有的 Part A 的测试，并且和以往一样，使用 `make handin` 去提交它。如果你想尝试找出为什么一些特定的测试是失败的，可以运行 `run ./grade-lab4 -v`，它将向你展示内核构建的输出，和测试失败时的 QEMU 运行情况。当测试失败时，这个脚本将停止运行，然后你可以去检查 `jos.out` 的内容，去查看内核真实的输出内容。

### Part B：写时复制 Fork

正如在前面提到过的，Unix 提供 `fork()` 系统调用作为它主要的进程创建原语。`fork()` 系统调用通过复制调用进程（父进程）的地址空间来创建一个新进程（子进程）。

xv6 Unix 的 `fork()` 从父进程的页上复制所有数据，然后将它分配到子进程的新页上。从本质上看，它与 `dumbfork()` 所采取的方法是相同的。复制父进程的地址空间到子进程，是 `fork()` 操作中代价最高的部分。

但是，一个对 `fork()` 的调用后，经常是紧接着几乎立即在子进程中有一个到 `exec()` 的调用，它使用一个新程序来替换子进程的内存。这是 shell 默认去做的事，在这种情况下，在复制父进程地址空间上花费的时间是非常浪费的，因为在调用 `exec()` 之前，子进程使用的内存非常少。

基于这个原因，Unix 的最新版本利用了虚拟内存硬件的优势，允许父进程和子进程去共享映射到它们各自地址空间上的内存，直到其中一个进程真实地修改了它们为止。这个技术就是众所周知的“写时复制”。为实现这一点，在 `fork()` 时，内核将复制从父进程到子进程的地址空间的映射，而不是所映射的页的内容，并且同时设置正在共享中的页为只读。当两个进程中的其中一个尝试去写入到它们共享的页上时，进程将产生一个页故障。在这时，Unix 内核才意识到那个页实际上是“虚拟的”或“写时复制”的副本，然后它生成一个新的、私有的、那个发生页故障的进程可写的、页的副本。在这种方式中，个人的页的内容并不进行真实地复制，直到它们真正进行写入时才进行复制。这种优化使得一个`fork()` 后在子进程中跟随一个 `exec()` 变得代价很低了：子进程在调用 `exec()` 时或许仅需要复制一个页（它的栈的当前页）。

在本实验的下一段中，你将实现一个带有“写时复制”的“真正的”类 Unix 的 `fork()`，来作为一个常规的用户空间库。在用户空间中实现 `fork()` 和写时复制有一个好处就是，让内核始终保持简单，并且因此更不易出错。它也让个别的用户模式程序在 `fork()` 上定义了它们自己的语义。一个有略微不同实现的程序（例如，代价昂贵的、总是复制的 `dumbfork()` 版本，或父子进程真实共享内存的后面的那一个），它自己可以很容易提供。

#### 用户级页故障处理

一个用户级写时复制 `fork()` 需要知道关于在写保护页上的页故障相关的信息，因此，这是你首先需要去实现的东西。对用户级页故障处理来说，写时复制仅是众多可能的用途之一。

它通常是配置一个地址空间，因此在一些动作需要时，那个页故障将指示去处。例如，主流的 Unix 内核在一个新进程的栈区域中，初始的映射仅是单个页，并且在后面“按需”分配和映射额外的栈页，因此，进程的栈消费是逐渐增加的，并因此导致在尚未映射的栈地址上发生页故障。在每个进程空间的区域上发生一个页故障时，一个典型的 Unix 内核必须对它的动作保持跟踪。例如，在栈区域中的一个页故障，一般情况下将分配和映射新的物理内存页。一个在程序的 BSS 区域中的页故障，一般情况下将分配一个新页，然后用 0 填充它并映射它。在一个按需分页的系统上的一个可执行文件中，在文本区域中的页故障将从磁盘上读取相应的二进制页并映射它。

内核跟踪有大量的信息，与传统的 Unix 方法不同，你将决定在每个用户空间中关于每个页故障应该做的事。用户空间中的 bug 危害都较小。这种设计带来了额外的好处，那就是允许程序员在定义它们的内存区域时，会有很好的灵活性；对于映射和访问基于磁盘文件系统上的文件时，你应该使用后面的用户级页故障处理。

##### 设置页故障服务程序

为了处理它自己的页故障，一个用户环境将需要在 JOS 内核上注册一个页故障服务程序入口。用户环境通过新的 `sys_env_set_pgfault_upcall` 系统调用来注册它的页故障入口。我们给结构 `Env` 增加了一个新的成员 `env_pgfault_upcall`，让它去记录这个信息。

>
> **练习 8**、实现 `sys_env_set_pgfault_upcall` 系统调用。当查找目标环境的环境 ID 时，一定要确认启用了权限检查，因为这是一个“危险的”系统调用。 “`
>
>
>

##### 在用户环境中的正常和异常栈

在正常运行期间，JOS 中的一个用户环境运行在正常的用户栈上：它的 `ESP` 寄存器开始指向到 `USTACKTOP`，而它所推送的栈数据将驻留在 `USTACKTOP-PGSIZE` 和 `USTACKTOP-1`（含）之间的页上。但是，当在用户模式中发生页故障时，内核将在一个不同的栈上重新启动用户环境，运行一个用户级页故障指定的服务程序，即用户异常栈。其它，我们将让 JOS 内核为用户环境实现自动的“栈切换”，当从用户模式转换到内核模式时，x86 处理器就以大致相同的方式为 JOS 实现了栈切换。

JOS 用户异常栈也是一个页的大小，并且它的顶部被定义在虚拟地址 `UXSTACKTOP` 处，因此用户异常栈的有效字节数是从 `UXSTACKTOP-PGSIZE` 到 `UXSTACKTOP-1`（含）。尽管运行在异常栈上，用户页故障服务程序能够使用 JOS 的普通系统调用去映射新页或调整映射，以便于去修复最初导致页故障发生的各种问题。然后用户级页故障服务程序通过汇编语言 `stub` 返回到原始栈上的故障代码。

每个想去支持用户级页故障处理的用户环境，都需要为它自己的异常栈使用在 Part A 中介绍的 `sys_page_alloc()` 系统调用去分配内存。

##### 调用用户页故障服务程序

现在，你需要去修改 `kern/trap.c` 中的页故障处理代码，以能够处理接下来在用户模式中发生的页故障。我们将故障发生时用户环境的状态称之为捕获时状态。

如果这里没有注册页故障服务程序，JOS 内核将像前面那样，使用一个消息来销毁用户环境。否则，内核将在异常栈上设置一个陷阱帧，它看起来就像是来自 `inc/trap.h` 文件中的一个 `struct UTrapframe` 一样：

```
                      <-- UXSTACKTOP
    trap-time esp
    trap-time eflags
    trap-time eip
    trap-time eax     start of struct PushRegs
    trap-time ecx
    trap-time edx
    trap-time ebx
    trap-time esp
    trap-time ebp
    trap-time esi
    trap-time edi      end of struct PushRegs
    tf_err (error code)
    fault_va           <-- %esp when handler is run

```

然后，内核安排这个用户环境重新运行，使用这个栈帧在异常栈上运行页故障服务程序；你必须搞清楚为什么发生这种情况。`fault_va` 是引发页故障的虚拟地址。

如果在一个异常发生时，用户环境已经在用户异常栈上运行，那么页故障服务程序自身将会失败。在这种情况下，你应该在当前的 `tf->tf_esp` 下，而不是在 `UXSTACKTOP` 下启动一个新的栈帧。

去测试 `tf->tf_esp` 是否已经在用户异常栈上准备好，可以去检查它是否在 `UXSTACKTOP-PGSIZE` 和 `UXSTACKTOP-1`（含）的范围内。

>
> **练习 9**、实现在 `kern/trap.c` 中的 `page_fault_handler` 的代码，要求派发页故障到用户模式故障服务程序上。在写入到异常栈时，一定要采取适当的预防措施。（如果用户环境运行时溢出了异常栈，会发生什么事情？）
>
>
>

##### 用户模式页故障入口点

接下来，你需要去实现汇编程序，它将调用 C 页故障服务程序，并在原始的故障指令处恢复程序运行。这个汇编程序是一个故障服务程序，它由内核使用 `sys_env_set_pgfault_upcall()` 来注册。

>
> **练习 10**、实现在 `lib/pfentry.S` 中的 `_pgfault_upcall` 程序。最有趣的部分是返回到用户代码中产生页故障的原始位置。你将要直接返回到那里，不能通过内核返回。最难的部分是同时切换栈和重新加载 EIP。
>
>
>

最后，你需要去实现用户级页故障处理机制的 C 用户库。

>
> **练习 11**、完成 `lib/pgfault.c` 中的 `set_pgfault_handler()`。 ”`
>
>
>

##### 测试

运行 `user/faultread`（make run-faultread）你应该会看到：

```
    ...
    [00000000] new env 00001000
    [00001000] user fault va 00000000 ip 0080003a
    TRAP frame ...
    [00001000] free env 00001000
```

运行 `user/faultdie` 你应该会看到：

```
    ...
    [00000000] new env 00001000
    i faulted at va deadbeef, err 6
    [00001000] exiting gracefully
    [00001000] free env 00001000
```

运行 `user/faultalloc` 你应该会看到：

```
    ...
    [00000000] new env 00001000
    fault deadbeef
    this string was faulted in at deadbeef
    fault cafebffe
    fault cafec000
    this string was faulted in at cafebffe
    [00001000] exiting gracefully
    [00001000] free env 00001000
```

如果你只看到第一个 “this string” 行，意味着你没有正确地处理递归页故障。

运行 `user/faultallocbad` 你应该会看到：

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

确保你理解了为什么 `user/faultalloc` 和 `user/faultallocbad` 的行为是不一样的。

>
> **小挑战！**扩展你的内核，让它不仅是页故障，而是在用户空间中运行的代码能够产生的所有类型的处理器异常，都能够被重定向到一个用户模式中的异常服务程序上。写出用户模式测试程序，去测试各种各样的用户模式异常处理，比如除零错误、一般保护故障、以及非法操作码。
>
>
>

#### 实现写时复制 Fork

现在，你有个内核功能要去实现，那就是在用户空间中完整地实现写时复制 `fork()`。

我们在 `lib/fork.c` 中为你的 `fork()` 提供了一个框架。像 `dumbfork()`、`fork()` 应该会创建一个新环境，然后通过扫描父环境的整个地址空间，并在子环境中设置相关的页映射。重要的差别在于，`dumbfork()` 复制了页，而 `fork()` 开始只是复制了页映射。`fork()` 仅当在其中一个环境尝试去写入它时才复制每个页。

`fork()` 的基本控制流如下：

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()` 在一个临时位置分配一个映射的新页，并将故障页的内容复制进去。然后，故障服务程序以读取/写入权限映射新页到合适的地址，替换旧的只读映射。

对于上面的几个操作，用户级 `lib/fork.c` 代码必须查询环境的页表（即，那个页的 PTE 是否标记为 `PET_COW`）。为此，内核在 `UVPT` 位置精确地映射环境的页表。它使用一个 [聪明的映射技巧](https://pdos.csail.mit.edu/6.828/2018/labs/lab4/uvpt.html) 去标记它，以使用户代码查找 PTE 时更容易。`lib/entry.S` 设置 `uvpt` 和 `uvpd`，以便于你能够在 `lib/fork.c` 中轻松查找页表信息。

>
> **练习 12**、在 `lib/fork.c` 中实现 `fork`、`duppage` 和 `pgfault`。
>
>
> 使用 `forktree` 程序测试你的代码。它应该会产生下列的信息，在信息中会有 ‘new env'、'free env'、和 'exiting gracefully’ 这样的字眼。信息可能不是按如下的顺序出现的，并且环境 ID 也可能不一样。
>
>
>
> ```
>         1000: I am ''
>         1001: I am '0'
>         2000: I am '00'
>         2001: I am '000'
>         1002: I am '1'
>         3000: I am '11'
>         3001: I am '10'
>         4000: I am '100'
>         1003: I am '01'
>         5000: I am '010'
>         4001: I am '011'
>         2002: I am '110'
>         1004: I am '001'
>         1005: I am '111'
>         1006: I am '101'
> ```
>
>

.

>
> **小挑战！**实现一个名为 `sfork()` 的共享内存的 `fork()`。这个版本的 `sfork()` 中，父子环境共享所有的内存页（因此，一个环境中对内存写入，就会改变另一个环境数据），除了在栈区域中的页以外，它应该使用写时复制来处理这些页。修改 `user/forktree.c` 去使用 `sfork()` 而是不常见的 `fork()`。另外，你在 Part C 中实现了 IPC 之后，使用你的 `sfork()` 去运行 `user/pingpongs`。你将找到提供全局指针 `thisenv` 功能的一个新方式。
>
>
>

.

>
> **小挑战！**你实现的 `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）

在实验 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`。

在 `inc/trap.h` 中，`IRQ_OFFSET` 被定义为十进制的 32。所以，IDT 条目 32 - 47 对应 IRQ 0 - 15。例如，时钟中断是 IRQ 0，所以 IDT[IRQ\_OFFSET+0]（即：IDT[32]）包含了内核中时钟中断服务程序的地址。这里选择 `IRQ_OFFSET` 是为了处理器异常不会覆盖设备中断，因为它会引起显而易见的混淆。（事实上，在早期运行 MS-DOS 的 PC 上， `IRQ_OFFSET` 事实上是 0，它确实导致了硬件中断服务程序和处理器异常处理之间的混淆！）

在 JOS 中，相比 xv6 Unix 我们做了一个重要的简化。当处于内核模式时，外部设备中断总是被关闭（并且，像 xv6 一样，当处于用户空间时，再打开外部设备的中断）。外部中断由 `%eflags` 寄存器的 `FL_IF` 标志位来控制（查看 `inc/mmu.h`）。当这个标志位被设置时，外部中断被打开。虽然这个标志位可以使用几种方式来修改，但是为了简化，我们只通过进程所保存和恢复的 `%eflags` 寄存器值，作为我们进入和离开用户模式的方法。

处于用户环境中时，你将要确保 `FL_IF` 标志被设置，以便于出现一个中断时，它能够通过处理器来传递，让你的中断代码来处理。否则，中断将被屏蔽或忽略，直到中断被重新打开后。我们使用引导加载程序的第一个指令去屏蔽中断，并且到目前为止，还没有去重新打开它们。

>
> **练习 13**、修改 `kern/trapentry.S` 和 `kern/trap.c` 去初始化 IDT 中的相关条目，并为 IRQ 0 到 15 提供服务程序。然后修改 `kern/env.c` 中的 `env_alloc()` 的代码，以确保在用户环境中，中断总是打开的。
>
>
> 另外，在 `sched_halt()` 中取消注释 `sti` 指令，以便于空闲的 CPU 取消屏蔽中断。
>
>
> 当调用一个硬件中断服务程序时，处理器不会推送一个错误代码。在这个时候，你可能需要重新阅读 [80386 参考手册](https://pdos.csail.mit.edu/6.828/2018/labs/readings/i386/toc.htm) 的 9.2 节，或 [IA-32 Intel 架构软件开发者手册 卷 3](https://pdos.csail.mit.edu/6.828/2018/labs/readings/ia32/IA32-3A.pdf) 的 5.8 节。
>
>
> 在完成这个练习后，如果你在你的内核上使用任意的测试程序去持续运行（即：`spin`），你应该会看到内核输出中捕获的硬件中断的捕获帧。虽然在处理器上已经打开了中断，但是 JOS 并不能处理它们，因此，你应该会看到在当前运行的用户环境中每个中断的错误属性并被销毁，最终环境会被销毁并进入到监视器中。
>
>
>

##### 处理时钟中断

在 `user/spin` 程序中，子环境首先运行之后，它只是进入一个高速循环中，并且内核再无法取得 CPU 控制权。我们需要对硬件编程，定期产生时钟中断，它将强制将 CPU 控制权返还给内核，在内核中，我们就能够将控制权切换到另外的用户环境中。

我们已经为你写好了对 `lapic_init` 和 `pic_init`（来自 `init.c` 中的 `i386_init`）的调用，它将设置时钟和中断控制器去产生中断。现在，你需要去写代码来处理这些中断。

>
> **练习 14**、修改内核的 `trap_dispatch()` 函数，以便于在时钟中断发生时，它能够调用 `sched_yield()` 去查找和运行一个另外的环境。
>
>
> 现在，你应该能够用 `user/spin` 去做测试了：父环境应该会 fork 出子环境，`sys_yield()` 到它许多次，但每次切换之后，将重新获得对 CPU 的控制权，最后杀死子环境后优雅地终止。
>
>
>

这是做回归测试的好机会。确保你没有弄坏本实验的前面部分，确保打开中断能够正常工作（即： `forktree`）。另外，尝试使用 `make CPUS=2 target` 在多个 CPU 上运行它。现在，你应该能够通过 `stresssched` 测试。可以运行 `make grade` 去确认。现在，你的得分应该是 65 分了（总分为 80）。

#### 进程间通讯（IPC）

（严格来说，在 JOS 中这是“环境间通讯” 或 “IEC”，但所有人都称它为 IPC，因此我们使用标准的术语。）

我们一直专注于操作系统的隔离部分，这就产生了一种错觉，好像每个程序都有一个机器完整地为它服务。一个操作系统的另一个重要服务是，当它们需要时，允许程序之间相互通讯。让程序与其它程序交互可以让它的功能更加强大。Unix 的管道模型就是一个权威的示例。

进程间通讯有许多模型。关于哪个模型最好的争论从来没有停止过。我们不去参与这种争论。相反，我们将要实现一个简单的 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）。

尝试去发送一个值时，一个环境使用接收者的 ID 和要发送的值去调用 `sys_ipc_try_send` 来发送。如果指定的环境正在接收（它调用了 `sys_ipc_recv`，但尚未收到值），那么这个环境将去发送消息并返回 0。否则将返回 `-E_IPC_NOT_RECV` 来表示目标环境当前不希望来接收值。

在用户空间中的一个库函数 `ipc_recv` 将去调用 `sys_ipc_recv`，然后，在当前环境的 `struct Env` 中查找关于接收到的值的相关信息。

同样，一个库函数 `ipc_send` 将去不停地调用 `sys_ipc_try_send` 来发送消息，直到发送成功为止。

##### 转移页

当一个环境使用一个有效的 `dstva` 参数（低于 `UTOP`）去调用 `sys_ipc_recv` 时，环境将声明愿意去接收一个页映射。如果发送方发送一个页，那么那个页应该会被映射到接收者地址空间的 `dstva` 处。如果接收者在 `dstva` 已经有了一个页映射，那么已存在的那个页映射将被取消映射。

当一个环境使用一个有效的 `srcva` 参数（低于 `UTOP`）去调用 `sys_ipc_try_send` 时，意味着发送方希望使用 `perm` 权限去发送当前映射在 `srcva` 处的页给接收方。在 IPC 成功之后，发送方在它的地址空间中，保留了它最初映射到 `srcva` 位置的页。而接收方也获得了最初由它指定的、在它的地址空间中的 `dstva` 处的、映射到相同物理页的映射。最后的结果是，这个页成为发送方和接收方共享的页。

如果发送方和接收方都没有表示要转移这个页，那么就不会有页被转移。在任何 IPC 之后，内核将在接收方的 `Env` 结构上设置新的 `env_ipc_perm` 字段，以允许接收页，或者将它设置为 0，表示不再接收。

##### 实现 IPC

>
> **练习 15**、实现 `kern/syscall.c` 中的 `sys_ipc_recv` 和 `sys_ipc_try_send`。在实现它们之前一起阅读它们的注释信息，因为它们要一起工作。当你在这些程序中调用 `envid2env` 时，你应该去设置 `checkperm` 的标志为 0，这意味着允许任何环境去发送 IPC 消息到另外的环境，并且内核除了验证目标 envid 是否有效外，不做特别的权限检查。
>
>
> 接着实现 `lib/ipc.c` 中的 `ipc_recv` 和 `ipc_send` 函数。
>
>
> 使用 `user/pingpong` 和 `user/primes` 函数去测试你的 IPC 机制。`user/primes` 将为每个质数生成一个新环境，直到 JOS 耗尽环境为止。你可能会发现，阅读 `user/primes.c` 非常有趣，你将看到所有的 fork 和 IPC 都是在幕后进行。
>
>
>

.

>
> **小挑战！**为什么 `ipc_send` 要循环调用？修改系统调用接口，让它不去循环。确保你能处理多个环境尝试同时发送消息到一个环境上的情况。
>
>
>

.

>
> **小挑战！**质数筛选是在大规模并发程序中传递消息的一个很巧妙的用法。阅读 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](https://swtch.com/%7Ersc/thread/squint.pdf) 中做了详细描述。实现了它的幂序列计算器，并且计算了 sin ( x + x <sup> 3)</sup> 的幂序列。
>
>
>

.

>
> **小挑战！**通过应用 Liedtke 的论文（[通过内核设计改善 IPC 性能](http://dl.acm.org/citation.cfm?id=168633)）中的一些技术、或你可以想到的其它技巧，来让 JOS 的 IPC 机制更高效。为此，你可以随意修改内核的系统调用 API，只要你的代码向后兼容我们的评级脚本就行。
>
>
>

**Part C 到此结束了。**确保你通过了所有的评级测试，并且不要忘了将你的小挑战的答案写入到 `answers-lab4.txt` 中。

在动手实验之前， 使用 `git status` 和 `git diff` 去检查你的更改，并且不要忘了去使用 `git add answers-lab4.txt` 添加你的小挑战的答案。在你全部完成后，使用 `git commit -am 'my solutions to lab 4’` 提交你的更改，然后 `make handin` 并关注它的动向。

---

via: <https://pdos.csail.mit.edu/6.828/2018/labs/lab4/>

作者：[csail.mit](https://pdos.csail.mit.edu) 选题：[lujun9972](https://github.com/lujun9972) 译者：[qhwdw](https://github.com/qhwdw) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


神奇的 Linux 命令行字符形状工具 boxes

>
> 本文将教你如何在 Linux 命令行终端中使用 boxes 工具绘制字符形状图形来包装你的文字让其更突出。
>
>
>

![](/data/attachment/album/201812/16/193631nj5mt5htxujjcjeh.png)

现在正值假期，每个 Linux 终端用户都该得到一点礼物。无论你是庆祝圣诞节还是庆祝其他节日，或者什么节日也没有，都没有关系。我将在接下来的几周内介绍 24 个 Linux 命令行小玩具，供你把玩或者与朋友分享。让我们享受乐趣，让这个月过得快乐一点，因为对于北半球来说，这个月有点冷并且沉闷。

对于我要讲述的内容，可能你之前就有些了解。但是，我还是希望我们都有机会学到一些新的东西（我做了一点研究，确保可以分享 24 个小玩具）。

24 个 Linux 终端小玩具中的第一个是叫做 `boxes` 的小程序。为何从 `boxes` 说起呢？因为在没有它的情况下很难将所有其他命令礼物包装起来！

在我的 Fedora 机器上，默认没有安装 `boxes` 程序，但它在我的普通仓库中可以获取到，所以用如下命令就可安装：

```
$ sudo dnf install boxes -y
```

如果你在使用其他 Linux 发行版，一般也都可以在默认仓库中找到 `boxes`。

`boxes` 是我真正希望在高中和大学计算机课程中就使用的实用程序，因为善意的老师要求我在每个源文件、函数、代码块等开头添加一些特定外观的备注信息。

```
/***************/
/* Hello World */
/***************/
```

事实证明，一旦你需要在框内添加几行文字，并且格式化的将它们统一风格就会变得很乏味。而 `boxes` 是一个简单实用程序，它使用 ASCII 艺术风格的字符形状框来包围文本。其字符形状默认风格是源代码注释风格，但也提供了一些其他选项。

它真的很容易使用。使用管道，便可以将一个简短问候语塞进字符形状盒子里。

```
$ cat greeting.txt | boxes -d diamonds -a c
```

上面的命令输出结果如下：

```
       /\          /\          /\
    /\//\\/\    /\//\\/\    /\//\\/\
 /\//\\\///\\/\//\\\///\\/\//\\\///\\/\
//\\\//\/\\///\\\//\/\\///\\\//\/\\///\\
\\//\/                            \/\\//
 \/                                  \/
 /\      I'm wishing you all a       /\
//\\     joyous holiday season      //\\
\\//     and a Happy Gnu Year!      \\//
 \/                                  \/
 /\                                  /\
//\\/\                            /\//\\
\\///\\/\//\\\///\\/\//\\\///\\/\//\\\//
 \/\\///\\\//\/\\///\\\//\/\\///\\\//\/
    \/\\//\/    \/\\//\/    \/\\//\/
       \/          \/          \/
```

或者玩点更有趣的，比如：

```
echo "I am a dog" | boxes -d dog -a c
```

不要惊讶，它将会输出如下：

```
          __   _,--="=--,_   __
         /  \."    .-.    "./  \
        /  ,/  _   : :   _  \/` \
        \  `| /o\  :_:  /o\ |\__/
         `-'| :="~` _ `~"=: |
            \`     (_)     `/
     .-"-.   \      |      /   .-"-.
.---{     }--|  /,.-'-.,\  |--{     }---.
 )  (_)_)_)  \_/`~-===-~`\_/  (_(_(_)  (
(              I am a dog               )
 )                                     (
'---------------------------------------'
```

`boxes` 程序提供了[很多选项](http://boxes.thomasjensen.com/examples.html) 用于填充、定位甚至处理正则表达式。你可以在其 [项目主页](https://boxes.thomasjensen.com/) 上了解更多有关 `boxes` 的信息，或者转到 [GitHub](https://github.com/ascii-boxes/boxes) 去下载源代码或者贡献你自己的盒子形状。说到此，如果你想给你的提交找个好点子，我已经有了一个想法：为什么不能是一个节日礼物盒子？

```
         _  _
        /_\/_\
 _______\_\/_/_______
|       ///\\\       |
|      ///  \\\      |
|                    |
|     "Happy pull    |
|       request!"    |
|____________________|
```

`boxes` 是基于 GPLv2 许可证的开源项目。

你有特别喜欢的命令行小玩具需要我介绍的吗？这个系列要介绍的小玩具大部分已经落实，但还预留了几个空位置。如果你有特别想了解的可以评论留言，我会查看的。如果还有空位置，我会考虑介绍它的。即使要介绍的小玩具已经有 24 个了，但如果我得到了一些很好的意见，我会在最后做一些有价值的提及。

你可以通过 [Drive a locomotive through your Linux terminal](https://opensource.com/article/18/12/linux-toy-sl) 来查看明天会介绍的命令行小玩具。

---

via: <https://opensource.com/article/18/12/linux-toy-boxes>

作者：[Jason Baker](https://opensource.com/users/jason-baker) 选题：[lujun9972](https://github.com/lujun9972) 译者：[jrg](https://github.com/jrglinux) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


从 Linux 终端查看笔记本电池状态和等级的 5 个方法

Summary: 在 Linux 上有很多这样的实用工具，而且可以在命令行使用。

在 Linux 上有很多这样的实用工具，而且可以在命令行使用。

![](/data/attachment/album/201812/16/201147imjo55telvtievof.jpg)

我们可以轻松地通过图形化界面查看当前电量百分比、是否在充电以及当前电量还可以使用多长时间等电池状态，但是却无法查看电池健康度等相关信息。

在这篇文章就是为了解决这些问题。

在 Linux 上有很多这样的实用工具，而且可以在命令行使用。

这篇文章今天就要探讨这个主题，我会尽我所能的覆盖尽可能多的信息。

每月检查一次你的电池健康度是一个很好的想法。它可以帮你检查你当前遇到的问题是否与电池或者充电相关。

同时，我们也可以查看电池模组名称、电源、厂商以及电池规格等。

电源管理是在不使用时关闭电源或者切换系统的组件到低耗模式的一种功能。

### 几种在 Linux 下检查电池状态的实用工具

* `upower`：是一个命令行工具，其提供了罗列系统中电源的接口。
* `acpi`：显示来自 `/proc` 或者 `/sys` 文件系统中的一些信息，例如电池状态或者热量信息。
* `batstat`：是一个为 Linux 打印电池状态的命令行工具。
* `tlp`：可以为你带来更高级的电源管理，而无需修改任何配置。
* `class file`：这个 sysfs 文件系统是一个提供了内核数据结构接口的伪文件系统。

### 如何使用 upower 命令检查笔记本电池状态

[upower](https://upower.freedesktop.org/) 是一个命令行工具，其提供了罗列系统中电源的接口。它在你的电脑上可以控制不同操作的延迟，这可以为你节省很大一部分电量。

只需要在 Linux 中运行以下命令获取电池以及它所依赖的其他信息。

```
$ upower -i /org/freedesktop/UPower/devices/battery_BAT0
  native-path:          BAT0
  vendor:               SMP
  model:                L14M4P23
  serial:               756
  power supply:         yes
  updated:              Monday 03 December 2018 07:56:18 PM IST (95 seconds ago)
  has history:          yes
  has statistics:       yes
  battery
    present:             yes
    rechargeable:        yes
    state:               discharging
    warning-level:       none
    energy:              28.23 Wh
    energy-empty:        0 Wh
    energy-full:         52.26 Wh
    energy-full-design:  60 Wh
    energy-rate:         10.714 W
    voltage:             14.819 V
    time to empty:       2.6 hours
    percentage:          54%
    capacity:            87.1%
    technology:          lithium-ion
    icon-name:          'battery-good-symbolic'
  History (charge):
    1543847178  54.000  discharging
  History (rate):
    1543847178  10.714  discharging
```

使用下面的格式检查电池的特定信息。

```
$ upower -i /org/freedesktop/UPower/devices/battery_BAT0 | grep -i "state\|percentage\|time to empty"
    state:               discharging
    time to empty:       2.1 hours
    percentage:          43%
```

这个类似于上面的那个，但是是在充电线缆的插入后运行，这也就是为什么下面会显示正在充电状态的原因。

```
$ upower -i /org/freedesktop/UPower/devices/battery_BAT0 | grep -i "state\|percentage\|time to empty"
    state:               charging
    percentage:          41%
```

### 如何使用 TLP 命令检查笔记本电池状态

TLP 是一个自由开源的多功能的命令行工具，它可以优化笔记本电池而无需修改任何配置。

TLP 可以为你的 Linux 带来更高级的电源管理，而无需理解任何技术细节。TLP 默认附带了一个已经为你的电池优化好的配置，所以你可以安装好后就不再管它了。尽管 TLP 是一个可以根据你的需求高度可定制的工具。

TLP 在绝大多数 Linux 发行版，例如 Arch、Debian、Fedora、Gentoo、openSUSE 等的官方库中都可用。使用你的 Linux 发行版的包管理安装 TLP 即可。

只需要在 Linux 中运行以下命令获取电池以及其他所依赖的信息。

```
$ sudo tlp-stat -b
--- TLP 1.1 --------------------------------------------

+++ Battery Status
/sys/class/power_supply/BAT0/manufacturer                   = SMP
/sys/class/power_supply/BAT0/model_name                     = L14M4P23
/sys/class/power_supply/BAT0/cycle_count                    = (not supported)
/sys/class/power_supply/BAT0/energy_full_design             =  60000 [mWh]
/sys/class/power_supply/BAT0/energy_full                    =  52260 [mWh]
/sys/class/power_supply/BAT0/energy_now                     =  21950 [mWh]
/sys/class/power_supply/BAT0/power_now                      =  10923 [mW]
/sys/class/power_supply/BAT0/status                         = Discharging

Charge                                                      =   42.0 [%]
Capacity                                                    =   87.1 [%]
```

也可以查看其他的信息。

```
$ sudo tlp-stat -s
--- TLP 1.1 --------------------------------------------

+++ System Info
System         = LENOVO Lenovo ideapad Y700-15ISK 80NV
BIOS           = CDCN35WW
Release        = "Manjaro Linux"
Kernel         = 4.19.6-1-MANJARO #1 SMP PREEMPT Sat Dec 1 12:21:26 UTC 2018 x86_64
/proc/cmdline  = BOOT_IMAGE=/boot/vmlinuz-4.19-x86_64 root=UUID=69d9dd18-36be-4631-9ebb-78f05fe3217f rw quiet resume=UUID=a2092b92-af29-4760-8e68-7a201922573b
Init system    = systemd
Boot mode      = BIOS (CSM, Legacy)

+++ TLP Status
State          = enabled
Last run       = 07:16:12  IST,   4362 sec(s) ago
Mode           = battery
Power source   = battery```

### 如何使用 ACPI 命令检查电池状态

ACPI 代表<ruby>高级配置和电源接口<rt>Advanced Configuration and Power Interface</rt></ruby>模块，它们是不同 ACPI 部件的内核模块。它们启用特殊的 ACPI 函数向 `/proc` 或者 `/sys` 中添加信息。这些信息可以通过事件或者其他监控程序的 acpid 进行解析。

```

$ acpi Battery 0: Charging, 43%, 01:05:11 until charged “`

查看电池容量。

```
$ acpi -i
Battery 0: Charging, 43%, 01:05:07 until charged
Battery 0: design capacity 3817 mAh, last full capacity 3324 mAh = 87%
```

查看更多有关电池及其相关的信息。

```
$ acpi -V
Battery 0: Charging, 43%, 01:05:07 until charged
Battery 0: design capacity 3815 mAh, last full capacity 3323 mAh = 87%
Adapter 0: on-line
Cooling 0: Processor 0 of 10
Cooling 1: Processor 0 of 10
Cooling 2: Processor 0 of 10
Cooling 3: iwlwifi 0 of 19
Cooling 4: Processor 0 of 10
Cooling 5: iwlwifi no state information available
Cooling 6: Processor 0 of 10
Cooling 7: Processor 0 of 10
Cooling 8: Processor 0 of 10
Cooling 9: intel_powerclamp no state information available
Cooling 10: x86_pkg_temp no state information available
Cooling 11: Processor 0 of 10
```

### 如何使用 Batstat 命令查看笔记本电池状态

`batstat` 是一个在 Linux 终端打印电池信息的命令行工具。

```
Status:                       Charging
Max energy:                   50.00 Wh
Energy left:                  24.50 Wh
Power Consumption:            26.40 W
Percentage left:              49.00%
Average power Consumption:    0.00 W
Time elapsed:                  0: 0:12 since 49.00%
= Time   ======== Percent ============================================
 0: 0: 0          49.00%
```

### 如何使用 sysfs 文件系统查看笔记本电池状态

sysfs 文件系统是一个提供了内核数据结构接口的伪文件系统。sysfs 下的文件提供有关设备、内核模块、文件系统和其他内核组件的信息。

sysfs 文件系统通常挂载在 `/sys`。通常来说，它会被系统自动挂载，但是也可以使用例如 `mount -t sysfs sysfs /sys` 命令进行手动挂载。

在 sysfs 文件系统中的很多文件都是只读的，但也有一些是可写的，允许更改内核变量。为了避免冗余，符号链接被大量用于连接文件系统数中的条目。

```
$ cat /sys/class/power_supply/BAT0/*
0
51
Normal
0
cat: /sys/class/power_supply/BAT0/device: Is a directory
52260000
60000000
26660000
SMP
L14M4P23
cat: /sys/class/power_supply/BAT0/power: Is a directory
27656000
1
  756
Charging
cat: /sys/class/power_supply/BAT0/subsystem: Is a directory
Li-ion
Battery
POWER_SUPPLY_NAME=BAT0
POWER_SUPPLY_STATUS=Charging
POWER_SUPPLY_PRESENT=1
POWER_SUPPLY_TECHNOLOGY=Li-ion
POWER_SUPPLY_CYCLE_COUNT=0
POWER_SUPPLY_VOLTAGE_MIN_DESIGN=14800000
POWER_SUPPLY_VOLTAGE_NOW=15840000
POWER_SUPPLY_POWER_NOW=27656000
POWER_SUPPLY_ENERGY_FULL_DESIGN=60000000
POWER_SUPPLY_ENERGY_FULL=52260000
POWER_SUPPLY_ENERGY_NOW=26660000
POWER_SUPPLY_CAPACITY=51
POWER_SUPPLY_CAPACITY_LEVEL=Normal
POWER_SUPPLY_MODEL_NAME=L14M4P23
POWER_SUPPLY_MANUFACTURER=SMP
POWER_SUPPLY_SERIAL_NUMBER=  756
14800000
15840000
```

---

via: <https://www.2daygeek.com/check-laptop-battery-status-and-charging-state-in-linux-terminal/>

作者：[Magesh Maruthamuthu](https://www.2daygeek.com/author/magesh/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[dianbanjiu](https://github.com/dianbanjiu) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


制定开源战略的免费指南

>
> 了解如何使用 TODO Group 的成熟实践，使您的组织的开源软件目标与您的业务目标保持一致。
>
>
>

![](/data/attachment/album/201812/16/203806ezd0j0tcatkjukkt.jpg)

大多数使用开源的公司都了解其商业价值，但他们可能缺乏战略性地实施开源计划和获得全部回报的工具。根据 [The New Stack](https://thenewstack.io/open-source-culture-starts-with-programs-and-policies/) 最近的一项调查，“开源计划的三大好处是 1）提高了对开源的认识，2）提高了开发周期的速度和灵活性，以及 3）更好的许可证合规性。”

运作一个开源计划办公室涉及到创建策略来帮助你定义和实施你的方法，并衡量你的进度。由 Linux 基金会与 TODO Group 合作开发的[企业开源指南](https://www.linuxfoundation.org/resources/open-source-guides/)基于多年的经验和实践提供了专业开源知识。

最新的指南中，[设置开源战略](https://www.linuxfoundation.org/resources/open-source-guides/setting-an-open-source-strategy/)详细介绍了制定战略和确保成功之路的基本步骤。根据该指南，“你的开源战略将管理、参与和创建开源软件的计划与计划所服务的业务目标联系起来。这可以开辟许多机会并促进创新。”该指南涵盖以下主题：

1. 为什么制定战略？
2. 你的战略文件
3. 战略方法
4. 关键考虑因素
5. 其他组成
6. 确定投资回报率
7. 投资目标

这里关键的第一步是创建和将你的开源策略形成文字，该策略将“帮助你最大限度地提高组织从开源中获得的利益。”同时，你详细的策略可以帮助你避免因错误而导致的困难，例如：选择错误的许可证或不正确地维护代码。根据指南，该文件还可以：

* 让领导者感到兴奋并参与
* 帮助在公司内获得支持
* 促进分散的多部门组织的决策
* 帮助建立一个健康的社区
* 解释贵公司的开源方式和对其使用的支持
* 明确贵公司在社区驱动的外部研发中投资的地方，以及贵公司将重点放在增值差异化的地方

Salesforce 的软件架构师兼本指南的撰稿人 Ian Varley 说：“在 Salesforce 内，我们有内部文件，我们将这些围绕开源战略指导和鼓励的文件分发给我们的工程团队。其中鼓励创建和使用开源，这让他们毫不含糊地知道公司的战略领导者完全支持它。此外，如果有某些我们不希望工程师使用的许可证或有其他开源指南，我们的内部文档需要明确。”

开源计划有助于促进企业文化，使企业更高效，并且根据指南，强有力的战略文档可以“帮助你的团队了解开源计划背后的业务目标，确保更好的决策，并最大限度地降低风险。”

了解如何使用[设置开源策略](https://www.linuxfoundation.org/resources/open-source-guides/setting-an-open-source-strategy/)新指南中的提示和经过验证的实践，将管理和创建开源软件的目标与组织的业务目标保持一致。然后，查看所有 12 个[企业开源指南](https://www.linuxfoundation.org/resources/open-source-guides/)，了解有关使用开源获得成功的更多信息。

本文最初发表在 [Linux基金会](https://www.linuxfoundation.org/blog/2018/11/a-free-guide-for-setting-your-open-source-strategy/)。

---

via: <https://www.linux.com/blog/2018/11/free-guide-setting-your-open-source-strategy>

作者：[Amber Ankerholz](https://www.linux.com/users/aankerholz) 选题：[lujun9972](https://github.com/lujun9972) 译者：[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


TLDR 页：Linux 手册页的简化替代品

Summary: 有时候，当你在寻找命令的基本信息时，man 就会显得有点庞杂。为了解决这个问题，人们创建了TLDR 页。

![](/data/attachment/album/201812/17/182300hb6i6gkkc4n44ngm.jpg)

在终端上使用各种命令执行重要任务是 Linux 桌面体验中不可或缺的一部分。Linux 这个开源操作系统拥有[丰富的命令](https://fossbytes.com/a-z-list-linux-command-line-reference/)，任何用户都无法全部记住所有这些命令。而使事情变得更复杂的是，每个命令都有自己的一组带来丰富的功能的选项。

为了解决这个问题，人们创建了<ruby> <a href="https://fossbytes.com/linux-lexicon-man-pages-navigation/">  手册页 </a> <rt>  man page </rt></ruby>，（手册 —— man 是 manual 的缩写）。首先，它是用英文写成的，包含了大量关于不同命令的深入信息。有时候，当你在寻找命令的基本信息时，它就会显得有点庞杂。为了解决这个问题，人们创建了[TLDR 页](https://github.com/tldr-pages/tldr)。

### 什么是 TLDR 页？

TLDR 页的 GitHub 仓库将其描述为简化的、社区驱动的手册页集合。在实际示例的帮助下，努力让使用手册页的体验变得更简单。如果还不知道，TLDR 取自互联网的常见俚语：<ruby> 太长没读 <rt>  Too Long Didn’t Read </rt></ruby>。

如果你想比较一下，让我们以 `tar` 命令为例。 通常，手册页的篇幅会超过 1000 行。`tar` 是一个归档实用程序，经常与 `bzip` 或 `gzip` 等压缩方法结合使用。看一下它的手册页：

[![tar man page](/data/attachment/album/201812/17/182301yb4vvlotbn2aa21v.jpg)](https://fossbytes.com/wp-content/uploads/2017/11/tar-man-page.jpg)

而另一方面，TLDR 页面让你只是浏览一下命令，看看它是如何工作的。 `tar` 的 TLDR 页面看起来像这样，并带有一些方便的例子 —— 你可以使用此实用程序完成的最常见任务：

[![tar tldr page](/data/attachment/album/201812/17/182302bec0dzlsm68vw8pc.jpg)](https://fossbytes.com/wp-content/uploads/2017/11/tar-tldr-page.jpg)

让我们再举一个例子，向你展示 TLDR 页面为 `apt` 提供的内容：

[![tldr-page-of-apt](/data/attachment/album/201812/17/182304amrk3y3nkkkkymr7.jpg)](https://fossbytes.com/wp-content/uploads/2017/11/tldr-page-of-apt.jpg)

如上，它向你展示了 TLDR 如何工作并使你的生活更轻松，下面让我们告诉你如何在基于 Linux 的操作系统上安装它。

### 如何在 Linux 上安装和使用 TLDR 页？

最成熟的 TLDR 客户端是基于 Node.js 的，你可以使用 NPM 包管理器轻松安装它。如果你的系统上没有 Node 和 NPM，请运行以下命令：

```
sudo apt-get install nodejs
sudo apt-get install npm
```

如果你使用的是 Debian、Ubuntu 或 Ubuntu 衍生发行版以外的操作系统，你可以根据自己的情况使用`yum`、`dnf` 或 `pacman`包管理器。

现在，通过在终端中运行以下命令，在 Linux 机器上安装 TLDR 客户端：

```
sudo npm install -g tldr
```

一旦安装了此终端实用程序，最好在尝试之前更新其缓存。 为此，请运行以下命令：

```
tldr  --update
```

执行此操作后，就可以阅读任何 Linux 命令的 TLDR 页面了。 为此，只需键入：

```
tldr  <commandname>
```

[![tldr kill command](/data/attachment/album/201812/17/182305nfggnzde556wggn1.jpg)](https://fossbytes.com/wp-content/uploads/2017/11/tldr-kill-command.jpg)

你还可以运行其[帮助命令](https://github.com/tldr-pages/tldr-node-client)，以查看可与 TLDR 一起使用的各种参数，以获取所需输出。 像往常一样，这个帮助页面也附有例子。

### TLDR 的 web、Android 和 iOS 版本

你会惊喜地发现 TLDR 页不仅限于你的 Linux 桌面。 相反，它也可以在你的 Web 浏览器中使用，可以从任何计算机访问。

要使用 TLDR Web 版本，请访问 [tldr.ostera.io](https://tldr.ostera.io/) 并执行所需的搜索操作。

或者，你也可以下载 [iOS](https://itunes.apple.com/us/app/tldt-pages/id1071725095?ls=1&mt=8) 和 [Android](https://play.google.com/store/apps/details?id=io.github.hidroh.tldroid) 应用程序，并随时随地学习新命令。

[![tldr app ios](/data/attachment/album/201812/17/182306qvccc9dba292429a.jpg)](https://fossbytes.com/wp-content/uploads/2017/11/tldr-app-ios.jpg)

你觉得这个很酷的 Linux 终端技巧很有意思吗？ 请尝试一下，让我们知道您的反馈。

---

via: <https://fossbytes.com/tldr-pages-linux-man-pages-alternative/>

作者：[Adarsh Verma](https://fossbytes.com/author/adarsh/) 译者：[wxy](https://github.com/wxy) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


学习人工智能如何入门

Summary: 在你开始从事人工智能之前，你需要先了解人类的智能。

在你开始从事人工智能之前，你需要先了解人类的智能。

>
> 在你开始从事人工智能之前，你需要先了解人类的智能。
>
>
>

![](/data/attachment/album/201812/17/193114l9yn4j90jj09c0ff.jpg)

我曾经问过别人、也被别人问过关于学习人工智能（AI）最好的方式是什么？我应该去阅读什么书？我应该去看什么视频？后面我将讲到这些，但是，考虑到人工智能涉及很多领域，我把这个问题分开来讲可能更好理解。

学习人工智能很重要的一点是区别开研究方面和应用方面。Google 的 Cassie Kozyrkov 在近日于伦敦举行的 O'Reilly 人工智能会议的一个演讲中 [描述了这个区别](https://www.youtube.com/watch?v=RLtI7r3QUyY)，并且这是一个很好的演讲。

人工智能研究在本质上是学术性的，在你能够获得人工智能的某些细节之前，需要大量的跨各类学科的数学知识。这部分的人工智能关注于算法和驱动人工智能发展的工具。比如，什么样的神经网络结构能够改善视觉识别的结果？我们如何使无监督学习成为更有用的方法？我们能否找到一个更好的方法，去理解深度学习流水线是如何得出答案的？

另一方面，人工智能应用更多是关于使用现有工具去获取有用的结果。开源在这里发挥了一个重要的作用，那就是免费提供了易于使用的、各种语言的软件。公有云提供商也致力于提供大量的机器学习、模型、以及数据集，这使得人工智能的入门比其它的要简单的多。

在这个问题上我想补充一点，那就是人工智能的从业者不应该将他们的工具视为神秘地输出答案的黑匣子。至少，他们应该去了解不同技术、模型、和数据采集方法的限制和潜在偏差。只是不需要去深入研究他们工具链中每个部分的理论基础。

虽然在日常工作中人工智能可能并不那么重要，但理解人工智能的大量的背景知识还是很有用的。人工智能已经超越了神经网络上深度学习的狭窄范围，目前神经网络上的强化学习和监督学习已经取得重要成就。例如，人工智能经常被视为是增强（而不是替代）人类判断和决策的一种方法。但是在机器和人类之间交换信息还有其自身的缺陷。

有了这些背景知识，下面是的一些研究领域和资源，你可能发现会很有用。

### 研究人工智能

在很多方面，用于人工智能研究的一个资源清单，可以反映出本科（甚至是研究生）的计算机科学项目都是专注于人工智能。最主要的区别是，你起草的教学大纲比起传统的大纲更关注于跨学科。

你的计算机科学和数学背景知识决定了你的起点。

如果你的计算机科学和数据背景知识很差或已经荒芜了，但你还希望能够深入了解人工智能的基本原理，那么从一些数学课程开始将会让你受益。MOOC 上像非盈利的 [edX](https://www.edx.org/) 平台和 [Coursera](https://www.coursera.org/) 上都有许多可供你选择的课程（这两个平台都对认证收费，但 edX 上所有的课程，对旁听者是全免费的）。

典型的基础课程包括：

* [MIT 的微积分课程](https://www.edx.org/course/calculus-1a-differentiation)，从微分开始学习
* [线性代数](https://www.edx.org/course/linear-algebra-foundations-to-frontiers) （德克萨斯大学）
* 概率与统计，比如 MIT 的 [概率 —— 不确定性与数据科学](https://courses.edx.org/courses/course-v1:MITx+6.431x+3T2018/course/)

从一个研究的角度去深入人工智能，你可能需要深入所有的这些数据领域，甚至更多。但是上面的内容应该让您在深入研究机器学习和AI之前大致了解可能是最重要的研究分支。

除了 MOOC 之外，像 [MIT OpenCourseWare](https://ocw.mit.edu/index.htm) 这样的资源也提供了大量的数学和计算机科学课程的大纲和各种支持材料。

有了这些基础，你就可以学习更专业的人工智能课程了。吴恩达从他在斯坦福大学时教的 “AI MOOC” 就是整个在线课程领域中最早流行起来的课程之一。今天，他的 [神经网络和深度学习](https://www.coursera.org/learn/neural-networks-deep-learning) 也是 Coursera 深度学习专业的一部分。在 edX 上也有相关的一些项目，比如，哥伦比亚大学提供的一个 [人工智能 MicroMasters](https://www.edx.org/micromasters/columbiax-artificial-intelligence)。

除了课程之外，也可以在网上找到各种范例和其它学习材料。这些包括：

* [神经网络和深度学习](http://neuralnetworksanddeeplearning.com/)
* MIT 出版的 Ian Goodfellow、Yoshua Bengio、Aaron Courville 的《[深度学习](http://www.deeplearningbook.org/)》

### 应用人工智能

人工智能应用更关注于使用可用的工具，而不是去构建新工具。对一些底层的数学，尤其是统计学的了解仍然是非常有用的 —— 甚至可以说是必需的 —— 但对这些知识的了解程度不像研究人工智能的要求那么高。

在这里编程是核心技能。虽然可以使用不同的编程语言去做，但是一些库和工具集 —— 比如 Python 的 [PyTorch](https://pytorch.org/)，依赖于 Python，所以这是一个应该掌握的好技能。尤其是，如果你有某种程度上的编程背景，MIT 的 [计算机科学入门和使用 Python 编程](https://www.edx.org/course/introduction-to-computer-science-and-programming-using-python)，它是基于 MIT 的 6.001 在校课程，是一个非常好的启蒙课程。如果你编程零基础，来自密歇根大学的 Charles Severance 的 [人人学编程（Python 使用入门）](https://www.coursera.org/learn/python) 是个很好的开端，它不会像 MIT 的课程那样，把你一下子扔进代码的汪洋大海。

[R 编程语言](https://www.r-project.org/about.html) 也是一个应该增加到你的技能库中的很有用的技能。虽然它在机器学习（ML）中使用的很少，但它在其它数据科学任务中很常见，并且经常与人工智能/机器学习和数据科学的应用实践结合在一起。例如，与组织和清理数据相关的许多任务同样适用于您最终使用的任何分析技术。像哈佛的 [数据科学认证](https://www.edx.org/professional-certificate/harvardx-data-science) 这样的一个 MOOC 系列就是一整套课程的一个例子，这些课程介绍了如何去很好地处理数据。

如果你从事人工智能方面的工作，那么你很可能会遇到的另一个开源软件库就是 [TensorFlow](https://www.tensorflow.org/)。它最初是由 Google 人工智能团队中的 Google 大脑团队的研发工程师开发的。[Google 提供了许多教程](https://www.tensorflow.org/tutorials/) 让你通过高级 Keras API 去开始使用 TensorFlow。你既可以在 Google 云上也可以在本地运行 TensorFlow。

通常，大的公有云提供商都提供在线数据集和易于使用的机器学习服务。但是，在你开始去 “玩” 数据集和应用之前，你需要考虑清楚，一旦开始选定一个提供商，你将被它们 “锁定” 的程度。

你的探索学习项目所需的数据集可以从许多不同的源获得。除了公有云提供商之外，[Kaggle](https://www.kaggle.com/) 是另一个受欢迎的源，总体来看，它也是一个比较好的学习源。以数字形式提供的政府数据也越来越多了。美国联邦政府的 [Data.gov](https://www.data.gov/) 声称它提供超过 300,000 个数据集。各州和地方政府也发布从餐馆健康评级到狗的名字的所有数据。

### 研究和应用人工智能兼而有之

最后我想说明的一点是，人工智能不仅是与数学、编程、数据有关的一个宽泛主题。人工智能作为一个综合体涉及到了许多其它的领域，包括心理学、语言学、博弈论、运筹学和控制系统。确实，现在有一些人工智能研究者担心，由于处理能力和大数据的结合，使得该领域过于关注最近才变得强大和有趣的少数几个技术。在了解人类如何学习和推理方面，许多长期存在的问题仍未解决。不管怎样，对这些广泛存在的问题有一个了解，将更好地让你在更广泛的背景中评估人工智能。

我比较喜欢的其中一个示例是杜克大学的 [人类和自治实验室](https://hal.pratt.duke.edu/)。这个实验室的工作涉及人机协同所面临的各种挑战，比如，如果自动化设备失效，自动驾驶仪如何设计才能让那些[“洋红色的孩子“](https://99percentinvisible.org/episode/children-of-the-magenta-automation-paradox-pt-1/) 快速取得控制。有一个基础的大脑科学课程，比如 MIT 的 [心理学导论](https://ocw.mit.edu/courses/brain-and-cognitive-sciences/9-00sc-introduction-to-psychology-fall-2011/)，它提供了关于人类智能和机器智能之间关系的一些很有用的内容。另一个类似的课程是，MIT 电子工程与计算机科学系已故教授 Marvin Minsky 的 [心灵的社会](https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-868j-the-society-of-mind-fall-2011/)。

关于学习人工智能，假如说有一个最重要的挑战，那它不是原材料和工具不易获得，因为它们有如此之多。我的目标并不是给你一个全面的指导，相反，而是指出了你可以去学习的不同路径，以及为你提供一些可能的起点。祝你学习愉快！

---

via: <https://opensource.com/article/18/12/how-get-started-ai>

作者：[Gordon Haff](https://opensource.com/users/ghaff) 选题：[lujun9972](https://github.com/lujun9972) 译者：[qhwdw](https://github.com/qhwdw) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


在 Linux 终端中开火车

Summary: 使用 sl 命令，你可以让自己驾驶火车，带来一个有趣的命令行体验。

使用 sl 命令，你可以让自己驾驶火车，带来一个有趣的命令行体验。

>
> 使用 sl 命令，你可以让自己驾驶火车，带来一个有趣的命令行体验。
>
>
>

![](/data/attachment/album/201812/17/214157u334qqdckk3yxkaq.png)

现在是 12 月，每个 Linux 终端用户都值得这一年的奖励。因此，我们将为你带来一个 Linux 命令行玩具的日历。什么是命令行玩具？它可能是一个游戏、一个小的无意义的打发时间的东西，或者为你在终端带来快乐的东西。

今天的 Linux 命令行玩具来自 Opensource.com 社区版主 [Ben Cotton](https://opensource.com/users/bcotton) 的建议。Ben 建议 `sl`，它是<ruby> 蒸汽机车 <rt>  steam locomotive </rt></ruby>的简称。

而对于 Linux `ls` 命令来说，`sl` 也是一个常见的拼写错误，这并不是巧合（LCTT 译注：意即 `sl` 是专门用来设计提醒 `ls` 打错的）。想要不再打错吗？尝试安装 `sl`。它可能已经在默认仓库中打包。对我而言，在 Fedora 中，这意味着安装起来很简单：

```
$ sudo dnf install sl -y
```

现在，只需键入 `sl` 即可测试。

![](/data/attachment/album/201812/17/214231ztqw6nzrf85p8pqw.gif)

你可能会像我一样注意到，`Ctrl+C` 不会让你的火车脱轨，所以你必须等待整列火车通过。这会让你知道打错了 `ls`！

想查看 `sl` 源码？它已经在[在 GitHub 上](https://github.com/mtoyoda/sl)。

`sl` 也是分享我个人关于开源许可证的见解的绝佳机会。虽然它的[许可证](https://github.com/mtoyoda/sl/blob/master/LICENSE)“足够开源”能够打包到我的发行版，但技术上而言，它并不是 [OSI 批准](https://opensource.org/licenses)的许可证。在其版权行之后，许可证的内容很简单：

>
> 每个人都可以在这个程序上做任何事情，包括复制，修改和改进，除非你试图假装你写了它。
>
>
> 即，上述版权声明必须出现在所有副本中。
>
>
> 作者对本软件不承担任何责任。
>
>
>

遗憾的是，当你选择未经 OSI 批准的许可证时，你可能会意外地为你的用户带来额外的工作，因为他们必须要弄清楚你的许可证是否适用于他们的情况。他们的公司政策是否允许他们做贡献？甚至他们可以合法地使用该程序吗？许可证是否与他们希望与之集成的其他程序的许可证相匹配？

除非你是律师（也许，即使你是律师），否则在非标准许可证范围内选择可能会很棘手。因此，如果你仍在寻找新年的方案，为什么不把仅 OSI 批准的许可证作为你 2019 年新项目的选择呢。

这并不是对作者的不尊重。`sl` 仍然是一个很棒的小命令行玩具。

你有一个你认为我应该介绍的最喜欢的命令行玩具吗？这个系列的日历大部分已经完成，但我还剩下几个空余。请在下面的评论中告诉我，我会了解一下。如果有空间，我会尝试包含它。如果没有，但我得到了一些好的投稿，我会在最后做一些荣誉介绍。

了解昨天的玩具，[在 Linux 命令行中装饰字符](https://opensource.com/article/18/12/linux-toy-boxes)，还有记得明天再来！

---

via: <https://opensource.com/article/18/12/linux-toy-sl>

作者：[Jason Baker](https://opensource.com/users/jason-baker) 选题：[lujun9972](https://github.com/lujun9972) 译者：[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


在 Linux 下交换 Ctrl 与大写锁定键

Summary: Linux 桌面环境使你可以根据需要轻松设置键盘。下面来演示如何去做。

Linux 桌面环境使你可以根据需要轻松设置键盘。下面来演示如何去做。

>
> Linux 桌面环境使你可以根据需要轻松设置键盘。下面来演示如何去做。
>
>
>

![](/data/attachment/album/201812/17/223900ek7wi7vvhabi7iav.jpg)

对于许多使用计算机很多年的用户来说，自从第一批 PC 键盘从生产线上下线后不久，Ctrl 和大写锁定键就已经在错误的位置上了。对我来说，这张 1995 年 Sun 工作站的老式键盘照片上的两个键的位置才是正确的。（原谅我放了一张模糊的图片，它是在昏暗的光线下使用 Minox 间谍相机拍摄的。）

![](/data/attachment/album/201812/17/223901ac7cczp33ir8xc2i.jpg)

感兴趣的话，可以读一下维基百科上对于 [Ctrl 键位置的历史](https://en.wikipedia.org/wiki/Control_key) 的介绍。我不打算讨论将 Ctrl 键放在“a”旁边而不是 Shift 键下方的各种理由，也不评论大写锁定键的无用性，也没有打算与那些主张使用手掌根来触发 Ctrl 键的人争论，即使在一些笔记本电脑键盘上不可能这样做到，因为有的键会位于腕托以下。

相反，我将假设我不是唯一喜欢把 Ctrl 键放在“a”旁边的人，并说明如何使用 Linux 自带的灵活性在各种桌面环境中交换 Ctrl 和大写锁定键的位置。请注意，下面的演示可能只有有限的有效期，因为调整桌面设置的方法经常发生变化，但我希望这为你开了一个好头。

### GNOME 3

[GNOME 3](https://www.gnome.org/gnome-3/) 桌面环境用户可以使用 [Tweaks](https://wiki.gnome.org/Apps/Tweaks) 工具交换大写锁定和 Ctrl 键，如下所示。

![](/data/attachment/album/201812/17/223916k64eueibnlx9jg6e.png)

具体步骤如下：

1. 从你的 Linux 发行版的软件仓库安装 Tweaks 工具。
2. 启动 Tweaks 程序。
3. 从左侧菜单中选择 “Keyboard & Mouse”。
4. 单击 “Additional Layout Options”。
5. 在打开的窗口中单击 “Ctrl position”，然后选择 “Swap Ctrl and Caps Lock”。

完成！顺便说一句，你可以使用 Tweaks 工具做很多很酷的事情。例如，我将我的右 Ctrl 键设置为 Compose 键，这让我可以使用键盘快捷键打出各种字符，例如通过 `Compose+c+,`、`Compose+e+'`、`Compose+o+^` 以及 `Compose+n+~` 分别键入 ç、é、ô 和 ñ。（LCTT 译注：可参考 [Special characters listed by extended compose sequence](https://www.ibm.com/support/knowledgecenter/en/SSKTWP_9.0.0/com.ibm.notes900.help.doc/acc_spec_characters_r.html)）

### KDE

我不使用 [KDE](https://www.kde.org/)，但我的同事 Seth Kenlon 写的 [将改变你的生命的 KDE tweaks](https://opensource.com/article/17/5/7-cool-kde-tweaks-will-improve-your-life) 这篇文章的第 5 项演示了如何重新映射按键。

### Xfce

据我所知，[Xfce](https://www.xfce.org/) 桌面环境没有一个方便的工具来管理这些（指交换按键）设置。 但是，`setxkbmap` 命令的 `ctrl:swapcaps` 选项可以帮助你完成交换按键的修改。这个修改包含两部分：

1. 弄清楚命令的用法；
2. 找出调用命令的位置，以便在桌面启动时激活它。

第一部分非常简单，命令是：

```
/usr/bin/setxkbmap -option "ctrl:nocaps"
```

在终端窗口中执行此命令，以确保结果符合你的预期。

假设上述命令有效，应该在哪里调用此命令呢？这需要一些实验。一种可能是在用户主目录的 `.profile` 文件中；另一个可能是将命令添加到 Xfce 的自启动配置（在设置管理器中查找 “Session and Startup”）里。

还有一种可能性是在文件 `/etc/default/keyboard` 中使用相同的选项，最终可能看起来像这样：

```
# KEYBOARD CONFIGURATION FILE

# Consult the keyboard(5) manual page.

XKBMODEL="pc105"
XKBLAYOUT="us"
XKBVARIANT=""
XKBOPTIONS="ctrl:swapcaps"

BACKSPACE="guess"
```

注意，这个更改将影响所有用户，因此如果你和其他人共享计算机，请准备好进行一些说明。此外，系统更新可能会覆盖此文件，因此如果你的设置失效了，就需要再次编辑它。将相同的信息放在用户主目录中的 `.keyboard` 文件内，可以为每个用户进行设置。

最后请注意，这些更改需要重新启动 Xfce（除非在终端窗口中的命令行上运行，但这在会话结束之后便会失效）。

### LXQt 和其他桌面环境

我没有用过 [LXQt](https://lxqt.org/)，但根据我使用 [LXDE](https://lxde.org/) 的经验，我会尝试上面用于 Xfce 的方法。我也希望适用于 Xfce 的方法可以用于其他 Linux 桌面环境。当然了，在其他桌面环境上遇到问题的时候，可以通过你最喜欢的搜索引擎来查找解决办法。

### 控制台

我没有在控制台上进行过尝试，因为我很少有机会与控制台（你在服务器上看到的或你的窗口系统没有正确显示时出现的界面）进行交互。上面给出的方法以人们希望的方式（即与其他应用程序一致）调整终端窗口。

但是，如果像上面一样已经编辑了 `/etc/default/keyboard` 文件或 `〜/.keyboard`，则实用程序 `setupcon` 可以用于更改控制台的键盘设置，以便实现相同的功能。[链接 1](https://askubuntu.com/questions/485454/how-to-remap-keys-on-a-user-level-both-with-and-without-x)、[链接 2](https://unix.stackexchange.com/questions/198791/how-do-i-permanently-change-the-console-tty-font-type-so-it-holds-after-reboot) 和 [链接 3](https://superuser.com/questions/290115/how-to-change-console-keymap-in-linux) 给出了一些关于如何从这两个文件实现这些更改的想法。第三个链接还讨论了使用 `dumpkeys` 和 `loadkeys` 来实现想要的效果。[setupcon 的手册](http://man.he.net/man1/setupcon) 简短而重要，值得阅读，再结合上面 StackExchange 问题的一些评论，应该足以得到一个解决办法。

### 其他环境

最后，上面 StackExchange 的链接中提到的这一点值得强调 —— 配置控制台与配置终端窗口不同；如前所述，后者是通过桌面管理器进行配置的。

`setxkbmap`、`xkeyboard-config`、`keyboard`、`console-setup` 和 `setupcon` 命令的手册都是有用的参考资料。或者，如果你不喜欢阅读手册，可以看一下 [这篇极好的文章](http://www.noah.org/wiki/CapsLock_Remap_Howto)。

---

via: <https://opensource.com/article/18/11/how-swap-ctrl-and-caps-lock-your-keyboard>

作者：[Chris Hermansen](https://opensource.com/users/clhermansen) 选题：[lujun9972](https://github.com/lujun9972) 译者：[jlztan](https://github.com/jlztan) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


90 年代的大学网管如何使用 Linux

![](/data/attachment/album/201812/18/110800anejj11nnqn8int8.jpg)

在上世纪 90 年代中期，我报名了计算机科学课。我大学的计算机科学系为学生提供了一台 SunOS 服务器，它是一个多用户、多任务的 Unix 系统。我们登录它并编写我们学习的编程语言代码，例如 C、C++ 和 ADA。在那时，在社交网络和 IM 出现之前，我们还使用该系统发送电子邮件和使用诸如 `write` 和 `talk` 之类的程序来相互通信。我们每个人被允许托管一个个人网站。我很高兴能够使用它完成我的作业并联系其他用户。

这是我第一次体验这种类型的操作环境，但我很快就了解了另一个可以做同样事情的操作系统：Linux。

那会我还是学生，我在大学找了份兼职工作。我的第一个职位是住房和住宅部（H＆R）的网络安装人员。这工作涉及到将学生宿舍与校园网络连接起来。由于这是该大学的第一个宿舍网络服务，因此只有两幢楼和大约 75 名学生连上了网。

在第二年，该网络扩展到另外两幢楼。H＆R 决定让该大学的信息技术办公室（OIT）管理这不断增长的业务。我进入 OIT 并开始担任 OIT 网络经理的学生助理。这就是我发现 Linux 的方式。我的新职责之一是管理防火墙系统，它为宿舍提供网络和互联网访问。

每个学生都注册了他们硬件的 MAC 地址。注册的学生可以连接到宿舍网络并获得 IP 地址及访问互联网。与大学使用的其他昂贵的 SunOS 和 VMS 服务器不同，这些防火墙使用运行着自由开源的 Linux 操作系统的低成本计算机。截至年底，该系统已注册近 500 名学生。

![Red hat Linux install disks](/data/attachment/album/201812/18/110845heq4xqs2xqs9ey55.png "Red hat Linux install disks")

OIT 网络工作人员使用 Linux 运行 HTTP、FTP 和其他服务。他们还在个人桌面上使用 Linux。就在那时，我意识到我手上的计算机看起来和运行起来就像 CS 系昂贵的 SunOS 机器一样，但没有高昂的成本。Linux 可以在商用 x86 硬件上运行，例如有 8 MB RAM 和 133Mhz Intel Pentium CPU 的 Dell Latitude。那对我来说是个卖点！我在从一个存货仓库中清理出来的机器上安装了 Red Hat Linux 5.2，并给了我的朋友登录帐户。

我使用我的新 Linux 服务器来托管我的网站并向我的朋友提供帐户，同时它还提供 CS 系服务器没有的图形功能。它使用了 X Windows 系统，我可以使用 Netscape Navigator 浏览网页，使用 [XMMS](http://www.xmms.org/) 播放音乐，并尝试不同的窗口管理器。我也可以下载并编译其他开源软件并编写自己的代码。

我了解到 Linux 提供了一些非常先进的功能，其中许多功能比更主流的操作系统更方便或更优越。例如，许多操作系统尚未提供应用更新的简单方法。在 Linux 中，这很简单，感谢 [autoRPM](http://www.ccp14.ac.uk/solution/linux/autorpm_redhat7_3.html)，一个由 Kirk Bauer 编写的更新管理器，它向 root 用户每日发送邮件，其中包含可用的更新。它有一个直观的界面，用于审查和选择要安装的软件更新 —— 这对于 90 年代中期来说非常了不起。

当时 Linux 可能并不为人所知，而且它经常受到怀疑，但我确信它会存活下来。而它确实生存下来了！

---

via: <https://opensource.com/article/18/5/my-linux-story-student>

作者：[Alan Formy-Duval](https://opensource.com/users/alanfdoss) 选题：[lujun9972](https://github.com/lujun9972) 译者：[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


极客漫画：TCP 兄弟

Summary: TCP 协议规定，在传输数据之前，要进行三次“握手”，来保证数据传输的可靠性。

TCP 协议规定，在传输数据之前，要进行三次“握手”，来保证数据传输的可靠性。

![](/data/attachment/album/201812/18/111345ick2lec5j5je2avj.png)

这幅漫画展示了 TCP 协议的沟通过程。首先是构建一个层（TCP 工作于传输层），然后向要通信的人发送建立联系的信号（SYN），接受到信息的人回复确认信息（ACK），确认已经收到，同时，发送建立联系的信息（SYN），这时，它发送的信息就是（SYN-ACK），当最初发送信息的人收到信息后，再回复了确认信息（ACK）。在回复了确认信息后，他们可以正常的交流，就开始说话了。

TCP 协议规定，在传输数据之前，要进行三次“握手”，来保证数据传输的可靠性。上面这幅漫画就展示了这样的内容。

如果你有兴趣查看更多信息，可以参看 [https://zh.wikipedia.org/wiki/传输控制协议](https://zh.wikipedia.org/wiki/%E4%BC%A0%E8%BE%93%E6%8E%A7%E5%88%B6%E5%8D%8F%E8%AE%AE)。

---

via: <https://turnoff.us/geek/tcp-buddies/>

作者：[Daniel Stori](http://turnoff.us/about/) 译者：[Bestony](https://github.com/bestony) 校对：[wxy](https://github.com/wxy) 合成&点评：[Bestony](https://github.com/Bestony)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


使用 lolcat 为你的 Linux 终端带来彩虹

Summary: 使用这个简单的工具，你可以为所需的任何程序的输出变成七彩。

使用这个简单的工具，你可以为所需的任何程序的输出变成七彩。

>
> 使用这个简单的工具，你可以为所需的任何程序的输出变成七彩。
>
>
>

![](/data/attachment/album/201812/18/160350ezquzucc15yuw4ru.png)

今天是 Linux 命令行玩具日历的第五天。如果这是你第一次访问该系列，你可能会问自己，什么是命令行玩具。即使我不太确定，但一般来说，它可能是一个游戏，或任何简单的可以帮助你在终端玩得开心的东西。

很可能你们中的一些人之前已经看过我们日历中的各种玩具，但我们希望每个人至少见到一件新事物。

今日的选择，`lolcat`，是我选择的第一个没有在我的 Linux 发行版中打包的程序，但它安装仍然很简单。它是一个 Ruby 程序，你应该可以使用下面的命令轻松地添加到系统中。

```
$ gem install lolcat
```

之后，只需将一些文本传送给它，就可以看到彩色的输出。例如，尝试几个之前在我们的日历中出现的程序，使用以下命令：

```
$ fortune | boxes -a c -d parchment | lolcat
```

根据你的运气，你可能会看到这样：

![](/data/attachment/album/201812/18/160350fdf1n3v1nst6mvlx.png)

你可以传递给 `lolcat` 一些参数。这里不再赘述，我建议你访问 `lolcat` 的 [GitHub 页面](https://github.com/busyloop/lolcat) 或者在终端输入 `lolcat --help` 了解。但一般来说，它们能设置彩虹的传递和频率，以及我个人最喜欢的动画。谁不喜欢终端的彩色动画输出呢？让我们再试一次，用一个不同的边框（当然是以猫为主题）和一句在我的格言列表中的适合猫的句子。

```
fortune -m "nine tails" | boxes -a c -d cat | lolcat -a
```

![](/data/attachment/album/201812/18/160353xncgcr2smwbvjzre.gif)

`lolcat` 是一个 BSD 许可下的开源软件。

你有特别喜欢的命令行小玩具需要我介绍的吗？这个系列要介绍的小玩具大部分已经有了落实，但还预留了几个空位置。如果你有特别想了解的可以评论留言，我会查看的。如果还有空位置，我会考虑介绍它的。如果没有，但如果我得到了一些很好的意见，我会在最后做一些有价值的提及。

了解一下昨天的玩具，[在 Linux 命令行中拥有一头牛](https://opensource.com/article/18/12/linux-toy-cowsay)，还有记得明天再来！

---

via: <https://opensource.com/article/18/12/linux-toy-lolcat>

作者：[Jason Baker](https://opensource.com/users/jason-baker) 选题：[lujun9972](https://github.com/lujun9972) 译者：[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


搜索 Linux 中的文件和文件夹的四种简单方法

Summary: Linux 管理员一天都不能离开搜索文件，因为这是他们的日常活动。

Linux 管理员一天都不能离开搜索文件，因为这是他们的日常活动。

![](/data/attachment/album/201812/18/232341e4bbp764944p7aan.jpg)

Linux 管理员一天都不能离开搜索文件，因为这是他们的日常活动。了解一些搜索的东西是不错的，因为这能帮助你在命令行服务器中工作。这些命令记忆起来不复杂，因为它们使用的是标准语法。

可以通过四个 Linux 命令啦执行此操作，每个命令都有自己独特的功能。

### 方法 1：使用 find 命令在 Linux 中搜索文件和文件夹

`find` 命令被广泛使用，并且是在 Linux 中搜索文件和文件夹的著名命令。它搜索当前目录中的给定文件，并根据搜索条件递归遍历其子目录。

它允许用户根据大小、名称、所有者、组、类型、权限、日期和其他条件执行所有类型的文件搜索。

运行以下命令以在系统中查找给定文件。

```
# find / -iname "sshd_config"
/etc/ssh/sshd_config
```

运行以下命令以查找系统中的给定文件夹。要在 Linux 中搜索文件夹，我们需要使用 `-type` 参数。

```
# find / -type d -iname "ssh"
/usr/lib/ssh
/usr/lib/go/src/cmd/vendor/golang.org/x/crypto/ssh
/usr/lib/go/pkg/linux_amd64/cmd/vendor/golang.org/x/crypto/ssh
/etc/ssh
```

使用通配符搜索系统上的所有文件。我们将搜索系统中所有以 `.config` 为扩展名的文件。

```
# find / -name "*.config"
/usr/lib/mono/gac/avahi-sharp/1.0.0.0__4d116c78973743f5/avahi-sharp.dll.config
/usr/lib/mono/gac/avahi-ui-sharp/0.0.0.0__4d116c78973743f5/avahi-ui-sharp.dll.config
/usr/lib/python2.7/config/Setup.config
/usr/share/git/mw-to-git/t/test.config
/var/lib/lightdm/.config
/home/daygeek/.config
/root/.config
/etc/skel/.config
```

使用以下命令格式在系统中查找空文件和文件夹。

```
# find / -empty
```

使用以下命令组合查找 Linux 上包含特定文本的所有文件。

```
# find / -type f -exec grep "Port 22" '{}' \; -print
# find / -type f -print | xargs grep "Port 22"
# find / -type f | xargs grep 'Port 22'
# find / -type f -exec grep -H 'Port 22' {} \;
```

### 方法 2：使用 locate 命令在 Linux 中搜索文件和文件夹

`locate` 命令比 `find` 命令运行得更快，因为它使用 `updatedb` 数据库，而 `find` 命令在真实系统中搜索。

它使用数据库而不是搜索单个目录路径来获取给定文件。

`locate` 命令未在大多数发行版中预安装，因此，请使用你的包管理器进行安装。

数据库通过 cron 任务定期更新，但我们可以通过运行以下命令手动更新它。

```
$ sudo updatedb
```

只需运行以下命令即可列出给定的文件或文件夹。在 `locate` 命令中不需要指定特定选项来打印文件或文件夹。

在系统中搜索 `ssh` 文件夹。

```
# locate --basename '\ssh'
/etc/ssh
/usr/bin/ssh
/usr/lib/ssh
/usr/lib/go/pkg/linux_amd64/cmd/vendor/golang.org/x/crypto/ssh
/usr/lib/go/src/cmd/go/testdata/failssh/ssh
/usr/lib/go/src/cmd/vendor/golang.org/x/crypto/ssh
```

在系统中搜索 `ssh_config` 文件。

```
# locate --basename '\sshd_config'
/etc/ssh/sshd_config
```

### 方法 3：在 Linux 中搜索文件使用 which 命令

`which` 返回在终端输入命令时执行的可执行文件的完整路径。

当你想要为可执行文件创建桌面快捷方式或符号链接时，它非常有用。

`which` 命令搜索当前用户而不是所有用户的 `$PATH` 环境变量中列出的目录。我的意思是，当你登录自己的帐户时，你无法搜索 root 用户文件或目录。

运行以下命令以打印 `vim` 可执行文件的完整路径。

```
# which vi
/usr/bin/vi
```

或者，它允许用户一次执行多个文件搜索。

```
# which -a vi sudo
/usr/bin/vi
/bin/vi
/usr/bin/sudo
/bin/sudo
```

### 方法 4：使用 whereis 命令在 Linux 中搜索文件

`whereis` 命令用于搜索给定命令的二进制、源码和手册页文件。

```
# whereis vi
vi: /usr/bin/vi /usr/share/man/man1/vi.1p.gz /usr/share/man/man1/vi.1.gz
```

---

via: <https://www.2daygeek.com/four-easy-ways-to-search-or-find-files-and-folders-in-linux/>

作者：[Prakash Subramanian](https://www.2daygeek.com/author/prakash/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


迁移到 Linux：网络和系统设置

>
> 这个系列我们提供了基础知识的概述，以帮助您成功地从另一个操作系统过渡到 Linux；这篇中我们涉及到 Linux 桌面系统上的一些常见设置。
>
>
>

![](/data/attachment/album/201812/19/004424z4994gs0ys9hsdt5.jpg)

在这个系列中，我们提供了基础知识的概述，以帮助您成功地从另一个操作系统过渡到 Linux。如果你错过了以前的文章，可以从这访问：

* [第1部分 - 入门介绍](/article-9212-1.html)
* [第2部分 - 磁盘、文件和文件系统](/article-9213-1.html)
* [第3部分 - 图形操作环境](/article-9293-1.html)
* [第4部分 - 命令行](/article-9565-1.html)
* [第5部分 - 使用 sudo](/article-9819-1.html)
* [第5部分 - 安装软件](/article-9823-1.html)

Linux 提供了一系列网络和系统设置。在你的桌面计算机上，Linux 允许您调整系统上的任何内容。大多数这些设置都出现在 `/etc` 目录下的纯文本文件中。这里我将介绍你使用桌面 Linux 操作系统的过程中最常用的设置。

大多数设置都能够在“设置”程序里面找到，这些设置可能对于不同的 Linux 发行版有所不同。通常来说，你可以修改背景、调整音量、连接打印机、进行显示设置等。对于这些设置尽管我不会全部谈论，但你可以自己探索。

### 连接互联网

在 Linux 中连接到互联网通常非常简单。如果您通过以太网电缆连接，Linux 通常会在插入电缆时或启动时（如果电缆已连接）获得 IP 地址并自动连接。

如果您使用无线网络，则在大多数发行版中都有一个菜单，可以在指示器面板中或在“设置”中（取决于您的发行版），您可以在其中选择无线网络的 SSID。如果网络受密码保护，它通常会提示您输入密码。然后连接，这个过程相当顺利。

在图形界面您可以通过进入设置来调整网络设置。通常称为“系统设置”或者是“设置”。通常可以轻松找到设置程序，因为它的图标是齿轮或工具图片（图1）。

![Network Settings](/data/attachment/album/201812/19/004439sjhslgghjbsqmmgh.png "Network Settings")

*图1： Gnome 桌面网络设置指示器图标。*

### 网络接口名称

在 Linux 下，网络设备有名称。 从历史上看，它们的名称分别为 eth0 和 wlan0 —— 或“以太网”和“无线网络”。 较新的 Linux 系统一直使用看起来更深奥的不同名称，如 enp4s0 和 wlp5s0 。 如果名称以 en 开头，则它是有线以太网接口。 如果它以 wl 开头，那么它就是一个无线接口。 其余的字母和数字反映了设备如何连接到硬件。

### 通过命令行进行网络管理

如果您希望更好地控制网络设置，或者如果您在没有图形桌面的情况下管理网络连接，则还可以从命令行管理网络。

请注意，用于在图形桌面中管理网络的最常用服务是“<ruby> 网络管理器 <rt>  Network Manager </rt></ruby>”，而网络管理器通常会覆盖在命令行上进行的设置更改。如果您正在使用网络管理器，最好在其界面中更改您的设置，以防止撤消您从命令行或其他位置所做的更改。

在图形环境中的更改设置与在网络管理器中很类似，您还可以使用名为 `nmtui` 的工具从命令行更改网络管理器设置。`nmtui` 工具提供了您在图形环境中找到的所有设置，但是是在基于文本的半图形界面中提供了该设置，该界面可在命令行上运行（图 2）。

![](/data/attachment/album/201812/19/004450npt0wwbewu68jabp.png)

*图 2：nmtui 界面*

在命令行上，有一个名为 `ifconfig` 的旧工具来管理网络，还有一个名为 `ip` 的新工具。在某些发行版中，`ifconfig` 被认为是不推荐使用的，默认情况下甚至没有安装。在其他发行版上，`ifconfig` 仍可以使用。

以下是一些允许您显示和更改网络设置的命令：

![](/data/attachment/album/201812/19/004453dvt96lvydz69vvhz.png)

### 进程和系统信息

在 Windows 系统中，你可以使用任务管理器来查看所有正在运行的程序和服务的列表。你可以停止运行中的程序，并且可以在其中显示的某些选项卡中查看系统性能。

在 Linux 系统下你可以使用命令行或者图形界面中做同样的事情。Linux 系统中根据你的发行版本会有不同的几个可用的图形工具。大多数所共有的工具是“系统监视器”和 KSysGuard。在这些工具中，你可以查看系统性能，查看进程列表甚至是杀死进程（图 3）。

![](/data/attachment/album/201812/19/004501fgw7zz5q2tq222up.png)

*图 3：NetHogs 截图*

在这些工具中，你也可以查看系统全局网络流量（图 4）。

![System Monitor](/data/attachment/album/201812/19/004509qe9ee1bepoppepwa.png "System Monitor")

*图 4：Gnome System Monitor 的截图*

### 管理进程和系统使用

您还可以从命令行使用相当多的工具。使用 `ps` 命令可以查看系统中的进程列表。默认情况下，这个命令的结果是显示当前终端会话下的所有进程列表。但是你也可以通过使用各种命令行参数显示其他进程。如果 `ps` 命令不会使用，可以使用命令 `info ps` 或者 `man ps` 获取帮助。

大多数人都希望得到一个进程列表，因为他们想要停止占用过多内存或 CPU 时间的进程。这种情况下有两个非常简单的命令，分别是 `top` 和 `htop` 命令（图 5）。

![](/data/attachment/album/201812/19/004522sxdndcmt1tndxxsc.png)

*图 5：top 截屏*

`top` 和 `htop` 工具使用效果非常相似。两个命令每秒或者两秒会更新重新排序，这样会把占用 CPU 资源最多的放置在列表顶部。你也可以根据其他资源的使用情况比如内存使用情况来排序。

使用这两个命令时（`top` 和 `htop`），你可以输入 `?` 来获取使用帮助，输入 `q` 来退出程序。使用 `top` 命令你可以按 `k` 键然后输入进程 ID 来杀死某个进程。

使用 `htop` 命令时你可以使用 `↑` `↓` 键来将列表中的一条记录进行高亮显示，按下 `F9` 键会杀死进程（需要回车确认）。

本系列中提供的信息和工具将帮助您开始使用 Linux。 只需一点时间和耐心，您就会感到这非常舒服。

想学习更多 Linux 内容可访问免费的 [Linux 简介](https://training.linuxfoundation.org/linux-courses/system-administration-training/introduction-to-linux)课程，此课程来自 Linux 基金会和 edx。

---

via: <https://www.linux.com/blog/learn/2018/4/migrating-linux-network-and-system-settings>

作者：[John Bonesio](https://www.linux.com/users/johnbonesio) 选题：[lujun9972](https://github.com/lujun9972) 译者：[ScarboroughCoral](https://github.com/ScarboroughCoral) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


认识存储：块、文件和对象

>
> 今天产生的大量数据带来了新的存储挑战。在本文中了解各种存储类型以及它们的使用方式。
>
>
>

![](/data/attachment/album/201812/19/154519p172q219w9nf2row.jpg)

现在，对于那些创建或消费数据的公司来说，处理数量巨大的生成数据是个非常大的挑战。而对于那些解决存储相关问题的科技公司来说，也是一个挑战。

Red Hat 存储首席产品营销经理 Michael St. Jean 说，“数据每年呈几何级增长，而我们发现数据大量增长的主要原因是由于消费增长和为拓展价值而进行的产业转型，毫无疑问，物联网对数据增长的贡献很大，但对软件定义存储来说最重要的挑战是，如何处理用户场景相关的数据增长。“

任何挑战都意味着机遇。Azure 存储、介质和边缘计算总经理 Tad Brockway 说，“今天，新旧数据源产生的海量数据为我们满足客户在规模、性能、灵活性、治理方面急剧增长的需求提供了一个机遇。”

### 现代软件定义存储的三种类型

这里有三个不同类型的存储解决方案 —— 块、文件、和对象 —— 虽然它们每个都可以与其它的共同工作，但它们每个都有不同的用途。

块存储是数据存储的最古老形式，数据都存储在固定长度的块或多个块中。块存储适用于企业存储环境，并且通常使用光纤通道或 iSCSI 接口。根据 SUSE 的软件定义存储高级产品经理 Larry Morris 的说法，“块存储要求一个应用去映射存储设备上存储数据块的位置。”

块存储在存储区域网和软件定义存储系统中是虚拟的，它是处于一个共享的硬件基础设施上的抽象逻辑设备，其创建和存在于服务器、虚拟服务器、或运行在基于像 SCSI、SATA、SAS、FCP、FCoE、或 iSCSI 这样的协议的系统管理程序上。

St. Jean 说“块存储将单个的存储卷（如一个虚拟或云存储节点、或一个老式硬盘）分割成单独的被称为块的实体。”

每个块独立存在，并且能够用它自己的数据传输协议和操作系统格式化 —— 给用户完全的配置自主权。由于块存储系统并不负责像文件存储系统那样的文件查找职责，所以，块存储是一个非常快的存储系统。由于同时具备速度和配置灵活性，使得块存储非常适合原始服务器存储或富媒体数据库。

块存储适合于宿主机操作系统、应用程序、数据库、完整虚拟机和容器。传统上，块存储仅能够被独立的机器访问，或呈现给集群中的机器访问。

### 基于文件的存储

基于文件的存储使用一个文件系统去映射存储设备上数据的存储位置。这种技术在直连或网络附加存储系统应用领域中处于支配地位。它需要做两件事情：组织数据并呈现给用户。St. Jean 说，”使用文件存储时，数据在服务器侧的存储方式与客户端用户所看到的是完全相同的。这就允许用户通过一些唯一标识符（像文件名、位置、或 URL）去请求一个文件，使用特定的数据传输协议与存储系统沟通。

其结果就是一种能够从上到下进行浏览的分层的文件结构。文件存储处于块存储之上，允许用户去查看和访问文件、文件夹这样的数据，但是被限制访问处于这些文件和文件夹之下的数据块。

Brockway 解释说，“文件存储一般用于像 NFS 和 CIFS/SMB 这种很多服务器基于 IP 网络进行访问的共享文件系统上。访问控制通过用户和组的权限实现在文件、目录和导出级别上。基于文件的存储可用于被多个用户和机器、二进制应用程序、数据库、虚拟机所需要的文件的存储上，以及容器上。“

### 对象存储

对象存储是最新的数据存储形式，它为非结构化数据提供一个仓库，它将内容从索引中分离出来，并允许多个文件连接到一个对象上。一个对象就是与任何相关元数据配对的一个数据块，这些元数据提供对象中包含的字节的上下文（比如数据创建时间和数据大小等）。也就是说这两样东西 —— 数据和元数据 —— 构成了一个对象。

对象存储的一个好处是每个数据块都关联了一个唯一标识符。访问数据需要唯一标识符，并且不需要应用程序或用户知道数据的真实存储位置。对象数据是通过 API 来访问的。

St. Jean 说，“对象中存储的数据是没有压缩和加密的，对象本身被组织在对象存储（一个填满其它对象的中心库）中或容器（包含应用程序运行所需要的所有文件的一个包）中。与文件存储系统的层次结构相比，对象、对象存储和容器在本质上是平面的 —— 这使得它们在存储规模巨大时访问速度很快。”

对象存储可以扩展到很多 PB 字节大小，以适应巨大的数据集，因此它是图像、音频、视频、日志、备份、和分析服务所使用的数据存储的最佳选择。

### 结论

现在你已经知道了各种类型的存储以及它们的用处。后面我们将继续研究这个主题的更多内容，敬请关注。

---

via: <https://www.linux.com/blog/2018/9/know-your-storage-block-file-object>

作者：[Swapnil Bhartiya](https://www.linux.com/users/arnieswap) 选题：[lujun9972](https://github.com/lujun9972) 译者：[qhwdw](https://github.com/qhwdw) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


SMPlayer：增强版的媒体播放器

Summary: SMPlayer 是一款自由开源媒体播放器，它基于强大的 MPlayer 媒体引擎。

VLC做得太好了，以至于对其他播放器没有需求。不过这个SMPlayer看起来也不错，赞！

VLC虽然好，但是很难找到完美的皮肤<br />
linux下面VLC的皮肤总是有各种各样的问题

gnome自带的totem装了解码器足够我用了@_@

win版的VLC全屏看某些视频会频繁出现变色

![SMPlayer default interface](/data/attachment/album/201812/19/174457nffi8ffybh111wuu.jpg)

当你要播放视频时，你会在[全新安装的 Ubuntu](https://itsfoss.com/things-to-do-after-installing-ubuntu-18-04/)，或其他许多发行版中，会注意到一个消息：

![](/data/attachment/album/201812/19/174458ek71ipkzwgwzz1fw.jpg)

*默认媒体播放器没有适合的编解码器*

这意味着系统上没有安装播放媒体的[所需编解码器](https://packages.ubuntu.com/trusty/ubuntu-restricted-extras)。现在，由于某些版权问题，某些基于 Linux 的操作系统无法在安装介质中预先打包编解码器。但是它们能让你只需点击即可下载和安装编解码器，或者你可以安装拥有所有媒体编解码器的媒体播放器。让我们了解一下 [SMPlayer](https://www.smplayer.info/)。

### 认识 SMPlayer：适用于 Linux 的更好的媒体播放器

SMPlayer 是一款自由开源媒体播放器，它基于强大的 [MPlayer](http://www.mplayerhq.hu/design7/news.html) 媒体引擎。SMPlayer 能够播放 avi、mp4、mkv、mpeg、mov、divx、h.264 以及其他任何主要媒体格式。锦上添花的是，它也可以播放 [YouTube](https://www.youtube.com/) 视频，并且无广告。

SMPlayer 是一个完整的媒体解决方案。它是跨平台的，因此可在所有操作系统上使用。如果你是双启动系统，则可以将其安装在 Windows 和 Linux 操作系统上，以便在两个系统上获得统一的体验。它还支持带触摸的可变形笔记本。

你也可以在 SMPlayer 上播放 YouTube。我知道每次复制粘贴视频 URL 并在外部播放器上播放是不切实际的。但是当你观看相对较长的视频时，SMPlayer 特别有用。SMPlayer 以相当好的质量播放 YouTube 视频，我觉得比在浏览器中播放得更好。通过在 SMPlayer 上播放较长的视频，你可以远离视频中间弹出的插播广告。

如果你在观看没有字幕的电影，你可以直接通过 SMPlayer 下载字幕。它集成了 [opensubtitles.org](https://www.opensubtitles.org/en/search)。所以，打开浏览器，搜索字幕，下载相应的字幕，解压缩，将它们放在视频文件夹中并将字幕连接到电影，这些都不需要！SMPlayer 会为你服务。

![Automatic subtitle download in SMPlayer](/data/attachment/album/201812/19/174459vqzdcioq7dp8kn0n.jpg)

SMPlayer 支持 30 多种语言，并可高度自定义。它还有应用主题和大量的图标集。

如果你觉得 SMPlayer 的默认界面看起来不太好，只需点击几下，它就可以看起来像这样：

![SMPlayer skin change](/data/attachment/album/201812/19/174501xrs637u9ur223m2m.jpg)

SMPlayer 为高级用户提供了许多工具和功能。它有均衡器、视频速度控制、宽高比和缩放控制、视频过滤器、屏幕截图等等。

总而言之，我真的很喜欢 SMPlayer。它在一个小巧轻量级的安装包中提供了很多功能。我认为它是 Linux PC 上必备的视频播放器。除了轻松播放所有媒体格式外，它还提供了大量的控制。

### 在 Linux 上安装 SMPlayer

SMPlayer 应该可在所有主要 Linux 发行版的软件中心获取。你可以搜索它并从那里安装它。

在 Ubuntu/ Linux Mint/ Elementary OS 上，你还可以通过在终端中运行以下命令来安装 SMPlayer

```
sudo apt install smplayer
```

或者，你可以在[这里](https://software.opensuse.org/download.html?project=home%3Asmplayerdev&package=smplayer)下载 Fedora、Arch Linux、OpenSUSE 和 Debian 的软件包。

### 总结

有很多像 VLC 媒体播放器那样成熟的播放器。SMPlayer 是拥有完整功能和插件优势的最佳产品之一。我认为它是[必备 Linux 应用](https://itsfoss.com/essential-linux-applications/)之一。

请尝试一下并在下面的评论栏与我们分享你的想法。

---

via: <https://itsfoss.com/smplayer/>

作者：[Aquil Roshan;Abhishek Prakash](https://itsfoss.com/author/aquil/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


极客漫画：聊天机器人

Summary: 对面和你聊天的甚至不是一条狗，而是一段代码。

对面和你聊天的甚至不是一条狗，而是一段代码。

![](/data/attachment/album/201812/19/182712jtecc8s8cq7l4lot.png)

现在聊天机器人技术使用的越来越多了，无论是 Slack 机器人还是 QQ 机器人，甚至还有图灵式的，比如 Siri、微软小冰。很多时候，群里面突然跑来一个机器人，那笨拙而无聊的话术，让人看了令人发噱。有时候，昨天我朋友表示，他的机器人被另外一个机器人搭讪了，然后由于本方的机器人太笨，对方色诱失败，哈哈。

前段时间听说，现在你接到的广告电话都不是人打的了，是自动语音机器人程序了 —— 而在几年前，国外就有一个人开发了一个语音对答机器人，以一个老人的角色和无数的电话销售人员（真人）聊得十分投入，有人到最后也没发现对方是个对答机器人。

所以，对面和你聊天的甚至不是一条狗，而是一段代码。

---

via: <http://turnoff.us/geek/chatbot/>

作者：[Daniel Stori](http://turnoff.us/about/) 译者&点评：[wxy](https://github.com/wxy) 校对&合成：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


如何不重装修复损坏的 Ubuntu 系统

真真是一个坑爹的解答，按照这个文章输入命令之后，我的系统被彻底搞坏了

thx  我的问题解决了 ：之前 /etc/ssh/sshd_config这个文件不存在  现在找回来了！

![](/data/attachment/album/201812/20/221412drfzewj3oj9z5mrb.jpg)

今天，我在升级我的 Ubuntu 18.04 LTS 系统。不幸的是，在更新 Ubuntu 时中途断电，系统关机。电源恢复后，我再次启动系统。在登录页面输入密码后，它变成空白并且没有响应。键盘和鼠标也没有作用。我只看到一个空白的屏幕！值得庆幸的是，它只是一台测试机，并且没有重要的数据。我可以直接擦除整个系统然后重新安装。但是，我不想这样做。由于我没有什么可失去的，我只是想不重装修复我损坏的 Ubuntu 系统，并且我成功了！如果你发现自己处于像我这样的境地，不要惊慌。这个简短的教程描述了如何在不丢失数据的情况下轻松修复损坏的 Ubuntu 系统，而无需重新安装。

### 修复损坏的 Ubuntu 系统

首先，尝试使用 live cd 登录并**在外部驱动器中备份数据**。以防这个方法没用，你仍然可以获取数据并重新安装系统！

在登录页上，按下 `CTRL+ALT+F1` 切换到 tty1。你可以在[此处](https://www.ostechnix.com/how-to-switch-between-ttys-without-using-function-keys-in-linux/)了解有关在 TTY 之间切换的更多信息。

现在，逐个输入以下命令来修复损坏的 Ubuntu Linux。

```
$ sudo rm /var/lib/apt/lists/lock
$ sudo rm /var/lib/dpkg/lock
$ sudo rm /var/lib/dpkg/lock-frontend
$ sudo dpkg --configure -a
$ sudo apt clean
$ sudo apt update --fix-missing
$ sudo apt install -f
$ sudo dpkg --configure -a
$ sudo apt upgrade
$ sudo apt dist-upgrade
```

最后，使用命令重启系统：

```
$ sudo reboot
```

你现在可以像往常一样登录到你的 Ubuntu 系统。

我做完这些步骤后，我 Ubuntu 18.04 测试系统中的所有数据都还在，一切都之前的一样。此方法可能不适用于所有人。但是，这个小小的技巧对我有用，并且比重装节省了一些时间。如果你了解其他更好的方法，请在评论区告诉我。我也会在本指南中添加它们。

这是这些了。希望这篇文章有用。

还有更多好东西。敬请关注！

干杯!

---

via: <https://www.ostechnix.com/how-to-fix-broken-ubuntu-os-without-reinstalling-it/>

作者：[SK](https://www.ostechnix.com/author/sk/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


在命令行中步入黑客帝国

没有dnf命令

来自辽宁沈阳的 Chrome Mobile 57.0|Android 8.1 用户

换成apt试试

换成apt试试

到底啥卵用？

>
> 使用 cmatrix 重建每个人都喜欢的 20 世纪 90 年代科幻电影中滚动代码的经典外观。
>
>
>

![](/data/attachment/album/201812/20/222508ds2z288zes8k28s7.png)

这是今天的命令行玩具日历推荐项目。如果这是你第一次访问该系列，你可能想知道什么是命令行玩具？它可以是在命令行中任何可以娱乐的东西，可以是一个游戏，一个有趣的工具，或者一个消遣的东西。

其中一些是经典，有些是全新的（至少对我而言），但我希望你们所有人都能在这个系列中找到你喜欢的东西。

在我们在接近下一年的时候，现在是回顾和期待的好时机。2019 年会为你带来什么？2019 年意味着什么？

我想起 2019 年将是我青少年时期最喜欢的科幻电影之一[黑客帝国](https://en.wikipedia.org/wiki/The_Matrix)的二十周年纪念日，它当时让我思考了未来将会发生什么。对于像我这样的痴迷计算机小孩来说，这是一个电脑程序员通过利用自己思维的力量崛起并成为虚拟宇宙中的动作英雄的终极故事。

当时，对我来说没有比这部电影更具未来感了。无论是故事本身，还是迷人的特效。即使意识到它是在二十多年前拍摄的也并没有改变我的想法。

今天将它带回我们的命令行玩具，让我们在终端用 `cmatrix` 重建黑客帝国中那向下滚动的代码流。 `cmatrix` 很容易安装，它在 Fedora 中被打包了，所以安装它只需：

```
$ dnf install cmatrix
```

接着，只需在你的终端输入 `cmatrix` 即可运行。

![](/data/attachment/album/201812/20/222534xb1r9md9mbrmuebh.gif)

你可以在 [GitHub](https://github.com/abishekvashok/cmatrix) 上找到使用 GPL 许可的 `cmatrix` 的源代码。

你有特别喜欢的命令行小玩具需要我介绍的吗？这个系列要介绍的小玩具大部分已经有了落实，但还预留了几个空位置。评论留言让我知道，我会查看的。如果还有空位置，我会考虑介绍它的。如果没有，但如果我得到了一些很好的意见，我会在最后做一些有价值的提及。

了解一下昨天的玩具，[在 Linux 中让 Bash 提示符变得像冬天](https://github.com/abishekvashok/cmatrix)，还有记得明天再来！

---

via: <https://opensource.com/article/18/12/linux-toy-cmatrix>

作者：[Jason Baker](https://opensource.com/users/jason-baker) 选题：[lujun9972](https://github.com/lujun9972) 译者：[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


我的个人电子邮件系统设置：notmuch、mbsync、Postfix 和 dovecot

![](/data/attachment/album/201812/21/071228kqaqj6khjq48k727.jpg)

我使用个人电子邮件系统已经相当长的时间了，但是一直没有记录过文档。最近我换了我的笔记本电脑（职业变更导致的变动），我在试图重新创建本地邮件系统时迷茫了。所以这篇文章是一个给自己看的文档，这样我不用费劲就能再次搭建出来。

### 服务器端

我运行自己的邮件服务器，并使用 Postfix 作为 SMTP 服务器，用 Dovecot 实现 IMAP。我不打算详细介绍如何配置这些设置，因为我的设置主要是通过使用 Jonas 为 Redpill 基础架构创建的脚本完成的。什么是 Redpill？（用 Jonas 自己的话说）：

>
> <jonas> Redpill 是一个概念：一种设置 Debian hosts 去跨组织协作的方式
>
>
> <jonas> 我发展了这个概念，并将其首次用于 Redpill 网中网：redpill.dk，其中涉及到了我自己的网络（jones.dk），我的主要客户的网络（homebase.dk），一个包括 Skolelinux Germany（free-owl.de）的在德国的网络，和 Vasudev 的网络（copyninja.info）
>
>
>

除此之外， 我还有一个 dovecot sieve 过滤，根据邮件的来源，对邮件进行高级分类，将其放到各种文件夹中。所有的规则都存在于每个有邮件地址的账户下的 `~/dovecot.sieve` 文件中。

再次，我不会详细介绍如何设置这些东西，因为这不是我这个帖子的目标。

### 在我的笔记本电脑上

在我的笔记本电脑上，我已经按照 4 个部分设置

1. 邮件同步：使用 `mbsync` 命令完成
2. 分类：使用 notmuch 完成
3. 阅读：使用 notmuch-emacs 完成
4. 邮件发送：使用作为中继服务器和 SMTP 客户端运行的 Postfix 完成。

### 邮件同步

邮件同步是使用 `mbsync` 工具完成的， 我以前是 OfflineIMAP 的用户，最近切换到 `mbsync`，因为我觉得它比 OfflineIMAP 的配置更轻量、更简单。该命令是由 isync 包提供的。

配置文件是 `~/.mbsyncrc`。下面是我的例子与一些个人设置。

```
IMAPAccount  copyninja
Host imap.copyninja.info
User vasudev
PassCmd      "gpg -q --for-your-eyes-only --no-tty --exit-on-status-write-error --batch --passphrase-file ~/path/to/passphrase.txt -d ~/path/to/mailpass.gpg"
SSLType IMAPS
SSLVersion TLSv1.2
CertificateFile /etc/ssl/certs/ca-certificates.crt

IMAPAccount gmail-kamathvasudev
Host imap.gmail.com
User kamathvasudev@gmail.com
PassCmd "gpg -q --for-your-eyes-only --no-tty --exit-on-status-write-error --batch --passphrase-file ~/path/to/passphrase.txt -d ~/path/to/mailpass.gpg"
SSLType IMAPS
SSLVersion TLSv1.2
CertificateFile /etc/ssl/certs/ca-certificates.crt

IMAPStore copyninja-remote
Account copyninja

IMAPStore gmail-kamathvasudev-remote
Account gmail-kamathvasudev

MaildirStore copyninja-local
Path ~/Mail/vasudev-copyninja.info/
Inbox ~/Mail/vasudev-copyninja.info/INBOX

MaildirStore gmail-kamathvasudev-local
Path ~/Mail/Gmail-1/
Inbox ~/Mail/Gmail-1/INBOX

Channel copyninja
Master :copyninja-remote:
Slave :copyninja-local:
Patterns *
Create Both
SyncState *
Sync All

Channel gmail-kamathvasudev
Master :gmail-kamathvasudev-remote:
Slave :gmail-kamathvasudev-local:
# Exclude everything under the internal [Gmail] folder, except the interesting folders
Patterns * ![Gmail]*
Create Both
SyncState *
Sync All
```

对上述配置中的一些有趣部分进行一下说明。一个是 PassCmd，它允许你提供 shell 命令来获取帐户的密码。这样可以避免在配置文件中填写密码。我使用 gpg 的对称加密，并在我的磁盘上存储密码。这当然是由 Unix ACL 保护安全的。

实际上，我想使用我的公钥来加密文件，但当脚本在后台或通过 systemd 运行时，解锁文件看起来很困难 （或者说几乎不可能）。如果你有更好的建议，我洗耳恭听:-)。

下一个指令部分是 Patterns。这使你可以有选择地同步来自邮件服务器的邮件。这对我来说真的很有帮助，可以排除所有的 “[Gmail]/ folders” 垃圾目录。

### 邮件分类

一旦邮件到达你的本地设备，我们需要一种方法来轻松地在邮件读取器中读取邮件。我最初的设置使用本地 dovecot 实例提供同步的 Maildir，并在 Gnus 中阅读。这种设置相比于设置所有的服务器软件是有点大题小作，但 Gnus 无法很好地应付 Maildir 格式，这是最好的方法。这个设置也有一个缺点，那就是在你快速搜索邮件时，要搜索大量邮件。而这就是 notmuch 的用武之地。

notmuch 允许我轻松索引上千兆字节的邮件档案而找到我需要的东西。我已经创建了一个小脚本，它结合了执行 `mbsync` 和 `notmuch`。我使用 dovecot sieve 来基于实际上创建在服务器端的 Maildirs 标记邮件。下面是我的完整 shell 脚本，它执行同步分类和删除垃圾邮件的任务。

```
#!/bin/sh

MBSYNC=$(pgrep mbsync)
NOTMUCH=$(pgrep notmuch)

if [ -n "$MBSYNC" -o -n "$NOTMUCH" ]; then
   echo "Already running one instance of mail-sync. Exiting..."
         exit 0
fi

echo "Deleting messages tagged as *deleted*"
notmuch search --format=text0 --output=files tag:deleted |xargs -0 --no-run-if-empty rm -v

echo "Moving spam to Spam folder"
notmuch search --format=text0 --output=files tag:Spam and \
  to:vasudev@copyninja.info | \
    xargs -0 -I {} --no-run-if-empty mv -v {} ~/Mail/vasudev-copyninja.info/Spam/cur
notmuch search --format=text0 --output=files tag:Spam and
  to:vasudev-debian@copyninja.info | \
     xargs -0 -I {} --no-run-if-empty mv -v {} ~/Mail/vasudev-copyninja.info/Spam/cur

MDIR="vasudev-copyninja.info vasudev-debian Gmail-1"
mbsync -Va
notmuch new

for mdir in $MDIR; do
    echo "Processing $mdir"
    for fdir in $(ls -d /home/vasudev/Mail/$mdir/*); do
      if [ $(basename $fdir) != "INBOX" ]; then
          echo "Tagging for $(basename $fdir)"
          notmuch tag +$(basename $fdir) -inbox -- folder:$mdir/$(basename $fdir)
      fi
    done
done
```

因此，在运行 `mbsync` 之前，我搜索所有标记为“deleted”的邮件，并将其从系统中删除。接下来，我在我的帐户上查找标记为“Spam”的邮件，并将其移动到“Spam”文件夹。你没看错，这些邮件逃脱了垃圾邮件过滤器进入到我的收件箱，并被我亲自标记为垃圾邮件。

运行 `mbsync` 后，我基于它们的文件夹标记邮件（搜索字符串 `folder:`）。这让我可以很容易地得到一个邮件列表的内容，而不需要记住列表地址。

### 阅读邮件

现在，我们已经实现同步和分类邮件，是时候来设置阅读部分。我使用 notmuch-emacs 界面来阅读邮件。我使用 emacs 的 Spacemacs 风格，所以我花了一些时间写了一个私有层，它将我所有的快捷键和分类集中在一个地方，而不会扰乱我的整个 `.spacemacs` 文件。你可以在 [notmuch-emacs-layer 仓库](https://source.copyninja.info/notmuch-emacs-layer.git/) 找到我的私有层的代码。

### 发送邮件

能阅读邮件这还不够，我们也需要能够回复邮件。而这是最近是我感到迷茫的一个略显棘手的部分，以至于不得不写这篇文章，这样我就不会再忘记了。（当然也不必在网络上参考一些过时的帖子。）

我的系统发送邮件使用 Postfix 作为 SMTP 客户端，使用我自己的 SMTP 服务器作为它的中继主机。中继的问题是，它不能是具有动态 IP 的主机。有两种方法可以允许具有动态 IP 的主机使用中继服务器， 一种是将邮件来源的 IP 地址放入 `my_network` 或第二个使用 SASL 身份验证。

我的首选方法是使用 SASL 身份验证。为此，我首先要为每台机器创建一个单独的账户，它将把邮件中继到我的主服务器上。想法是不使用我的主帐户 SASL 进行身份验证。（最初我使用的是主账户，但 Jonas 给出了可行的按账户的想法）

```
adduser <hostname>_relay
```

这里替换 `<hostname>` 为你的笔记本电脑的名称或任何你正在使用的设备。现在我们需要调整 Postfix 作为中继服务器。因此，在 Postfix 配置中添加以下行：

```
# SASL authentication
smtp_sasl_auth_enable = yes
smtp_tls_security_level = encrypt
smtp_sasl_tls_security_options = noanonymous
relayhost = [smtp.copyninja.info]:submission
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
```

因此， 这里的 `relayhost` 是用于将邮件转发到互联网的 Postfix 实例的服务器名称。`submission` 的部分 Postfix 将邮件转发到端口 587（安全端口）。`smtp_sasl_tls_security_options` 设置为不允许匿名连接。这是必须的，以便中继服务器信任你的移动主机，并同意为你转发邮件。

`/etc/postfix/sasl_passwd` 是你需要存储用于服务器 SASL 身份验证的帐户密码的文件。将以下内容放入其中。

```
[smtp.example.com]:submission user:password
```

用你已放入 `relayhost` 配置的 SMTP 服务器名称替换 `smtp.example.com`。用你创建的 `<hostname>_relay` 用户及其密码替换 `user` 和 `passwd`。

若要保护 `sasl_passwd` 文件，并为 Postfix 创建它的哈希文件，使用以下命令。

```
chown root:root /etc/postfix/sasl_passwd
chmod 0600 /etc/postfix/sasl_passwd
postmap /etc/postfix/sasl_passwd
```

最后一条命令将创建 `/etc/postfix/sasl_passwd.db` 文件，它是你的文件的 `/etc/postfix/sasl_passwd` 的哈希文件，具有相同的所有者和权限。现在重新加载 Postfix，并使用 `mail` 命令检查邮件是否从你的系统中发出。

### Bonus 的部分

好吧，因为我有一个脚本创建以上结合了邮件的同步和分类。我继续创建了一个 systemd 计时器，以定期同步后台的邮件。就我而言，每 10 分钟一次。下面是 `mailsync.timer` 文件。

```
[Unit]
Description=Check Mail Every 10 minutes
RefuseManualStart=no
RefuseManualStop=no

[Timer]
Persistent=false
OnBootSec=5min
OnUnitActiveSec=10min
Unit=mailsync.service

[Install]
WantedBy=default.target
```

下面是 mailsync.service 服务，这是 mailsync.timer 执行我们的脚本所需要的。

```
[Unit]
Description=Check Mail
RefuseManualStart=no
RefuseManualStop=yes

[Service]
Type=oneshot
ExecStart=/usr/local/bin/mail-sync
StandardOutput=syslog
StandardError=syslog
```

将这些文件置于 `/etc/systemd/user` 目录下并运行以下代码去开启它们：

```
systemctl enable --user mailsync.timer
systemctl enable --user mailsync.service
systemctl start --user mailsync.timer
```

这就是我从系统同步和发送邮件的方式。我从 Jonas Smedegaard 那里了解到了 afew，他审阅了这篇帖子。因此， 下一步， 我将尝试使用 afew 改进我的 notmuch 配置，当然还会有一个后续的帖子:-)。

---

via: <https://copyninja.info/blog/email_setup.html>

作者：[copyninja](https://copyninja.info) 译者：[lixinyuxx](https://github.com/lixinyuxx) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


用户、组及其它 Linux 特性

>
> Linux 和其他类 Unix 操作系统依赖于用户组，而不是逐个为用户分配权限和特权。一个组就是你想象的那样：一群在某种程度上相关的用户。
>
>
>

![](/data/attachment/album/201812/22/002103hsp4ilq194sllpz6.jpg)

到这个阶段，[在看到如何操作目录或文件夹之后](/article-10066-1.html)，但在让自己一头扎进文件之前，我们必须重新审视 权限、用户 和 组。幸运的是，[有一个网站上已经有了一个优秀而全面的教程，讲到了权限](https://www.linux.com/learn/understanding-linux-file-permissions)，所以你应该去立刻阅读它。简而言之，你使用权限来确定谁可以对文件和目录执行操作，以及他们可以对每个文件和目录执行什么操作 —— 从中读取、写入、移动、擦除等等。

要尝试本教程涵盖的所有内容，你需要在系统上创建新用户。让我们实践起来，为每一个需要借用你电脑的人创建一个用户，我们称之为 `guest` 账户。

**警告：** 例如，如果你错误地删除了自己的用户和目录，那么创建用户，特别是删除用户以及主目录会严重损坏系统。你可能不想在你日常的工作机中练习，那么请在另一台机器或者虚拟机上练习。无论你是否想要安全地练习，经常备份你的东西总是一个好主意。检查备份是否正常工作，为你自己以后避免很多咬牙切齿的事情。

### 一个新用户

你可以使用 `useradd` 命令来创建一个新用户。使用超级用户或 root 权限运行 `useradd`，即使用 `sudo` 或 `su`，这具体取决于你的系统，你可以：

```
sudo useradd -m guest
```

然后输入你的密码。或者也可以这样：

```
su -c "useradd -m guest"
```

然后输入 root 或超级用户的密码。

（ 为了简洁起见，我们将从现在开始假设你使用 `sudo` 获得超级用户或 root 权限。 ）

通过使用 `-m` 参数，`useradd` 将为新用户创建一个主目录。你可以通过列出 `/home/guest` 来查看其内容。

然后你可以使用以下命令来为新用户设置密码：

```
sudo passwd guest
```

或者你也可以使用 `adduser`，这是一个交互式的命令，它会询问你一些问题，包括你要为用户分配的 shell（是的，shell 有不止一种），你希望其主目录在哪里，你希望他们属于哪些组（有关这点稍后会讲到）等等。在运行 `adduser` 结束时，你可以设置密码。注意，默认情况下，在许多发行版中都没有安装 `adduser`，但安装了 `useradd`。

顺便说一下，你可以使用 `userdel` 来移除一个用户：

```
sudo userdel -r guest
```

使用 `-r` 选项，`userdel` 不仅删除了 `guest` 用户，还删除了他们的主目录和邮件中的条目（如果有的话）。

### 主目录中的内容

谈到用户的主目录，它依赖于你所使用的发行版。你可能已经注意到，当你使用 `-m` 选项时，`useradd` 使用子目录填充用户的目录，包括音乐、文档和诸如此类的内容以及各种各样的隐藏文件。要查看 `guest` 主目录中的所有内容，运行 `sudo ls -la /home/guest`。

进入新用户目录的内容通常是由 `/etc/skel` 架构目录确定的。有时它可能是一个不同的目录。要检查正在使用的目录，运行：

```
useradd -D
GROUP=100
HOME=/home
INACTIVE=-1
EXPIRE=
SHELL=/bin/bash
SKEL=/etc/skel
CREATE_MAIL_SPOOL=no
```

这会给你一些额外的有趣信息，但你现在感兴趣的是 `SKEL=/etc/skel` 这一行，在这种情况下，按照惯例，它指向 `/etc/skel/`。

由于 Linux 中的所有东西都是可定制的，因此你可以更改那些放入新创建的用户目录的内容。试试这样做：在 `/etc/skel/` 中创建一个新目录：

```
sudo mkdir /etc/skel/Documents
```

然后创建一个包含欢迎消息的文件，并将其复制过来：

```
sudo cp welcome.txt /etc/skel/Documents
```

现在删除 `guest` 账户:

```
sudo userdel -r guest
```

再次创建：

```
sudo useradd -m guest
```

嘿！你的 `Documents/` 目录和 `welcome.txt` 文件神奇地出现在了 `guest` 的主目录中。

你还可以在创建用户时通过编辑 `/etc/default/useradd` 来修改其他内容。我的看起来像这样：

```
GROUP=users
HOME=/home
INACTIVE=-1
EXPIRE=
SHELL=/bin/bash
SKEL=/etc/skel
CREATE_MAIL_SPOOL=no
```

这些选项大多数都是不言自明的，但让我们仔细看看 `GROUP` 选项。

### 群组心态

Linux 和其他类 Unix 操作系统依赖于用户组，而不是逐个为用户分配权限和特权。一个组就是你想象的那样：一群在某种程度上相关的用户。在你的系统上可能有一组允许使用打印机的用户，他们属于 `lp`（即 “line printer”）组。传统上 `wheel` 组的成员是唯一可以通过使用 `su` 成为超级用户或 root 的成员。`network` 用户组可以启动或关闭网络。还有许多诸如此类的。

不同的发行版有不同的组，具有相同或相似名称的组具有不同的权限，这也取决于你使用的发行版。因此，如果你在前一段中读到的内容与你系统中的内容不匹配，不要感到惊讶。

不管怎样，要查看系统中有哪些组，你可以使用：

```
getent group
```

`getent` 命令列出了某些系统数据库的内容。

要查找当前用户所属的组，尝试：

```
groups
```

当你使用 `useradd` 创建新用户时，除非你另行指定，否则用户将只属于一个组：他们自己。`guest` 用户属于 `guest` 组。组使用户有权管理自己的东西，仅此而已。

你可以使用 `groupadd` 命令创建新组，然后添加用户：

```
sudo groupadd photos
```

例如，这将创建 `photos` 组。下一次，我们将使用它来构建一个共享目录，该组的所有成员都可以读取和写入，我们将更多地了解权限和特权。敬请关注！

---

via: <https://www.linux.com/learn/intro-to-linux/2018/7/users-groups-and-other-linux-beasts>

作者：[Paul Brown](https://www.linux.com/users/bro66) 选题：[lujun9972](https://github.com/lujun9972) 译者：[MjSeven](https://github.com/MjSeven) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


持续集成与部署的 3 个最佳实践

Summary: 了解自动化，使用 Git 存储库以及参数化 Jenkins 管道。

了解自动化，使用 Git 存储库以及参数化 Jenkins 管道。

>
> 了解自动化，使用 Git 存储库以及参数化 Jenkins 管道。
>
>
>

![](/data/attachment/album/201812/22/095139y6ur04x8c4mx3e6r.png)

本文涵盖了三个关键主题：自动化 CI/CD 配置、使用 Git 存储库处理常见的 CI/CD 工件、参数化 Jenkins 管道。

### 术语

首先，我们定义一些术语。**CI/CD** 是允许团队快速自动化测试、打包、部署其应用程序的实践。它通常通过利用名为 [Jenkins](https://jenkins.io/) 的服务器来实现，该服务器充当 CI/CD 协调器。Jenkins 侦听特定输入（通常是代码签入后的 Git 挂钩），并在触发时启动一个管道。

<ruby> 管道 <rt>  pipeline </rt></ruby> 由开发和/或运营团队编写的代码组成，这些代码指导 Jenkins 在 CI/CD 过程中采取哪些操作。这个流水线通常类似于“构建我的代码，然后测试我的代码，如果这些测试通过，则把我的应用程序部署到下一个最高环境（通常是开发、测试或生产环境）”。组织通常具有更复杂的管道，并入了诸如工件存储库和代码分析器之类的工具，这里提供了一个高级示例。

现在我们了解了关键术语，让我们深入研究一些最佳实践。

### 1、自动化是关键

要在 PaaS 上运行 CI/CD，需要在集群上配置适当的基础设施。在这个例子中，我将使用 [OpenShift](https://www.openshift.com/)。

“Hello, World” 的实现很容易实现。简单地运行 `oc new-app jenkins-<persistent/ephemeral>`，然后，你就有了一个已经就绪的运行中的 Jenkins 服务器了。然而，在企业中的使用要复杂得多。除了 Jenkins 服务器之外，管理员通常还需要部署代码分析工具（如 SonarQube）和工件库（如 Nexus）。然后，它们必须创建管道来执行 CI/CD 和 Jenkins 从服务器，以减少主服务器的负载。这些实体中的大多数都由 OpenShift 资源支持，需要创建这些资源来部署所需的 CI/CD 基础设施。

最后，部署 CI/CD 组件所需要的手动步骤可能是需要重复进行的，而且你可能不想成为执行那些重复步骤的人。为了确保结果能够像以前一样快速、无错误和准确地产生，应该在创建基础设施的方式中结合自动化方法。这可以是一个 Ansible 剧本、一个 Bash 脚本，或者任何您希望自动化 CI/CD 基础设施部署的其它方式。我已经使用 [Ansible](https://docs.ansible.com/) 和 [OpenShift-Applier](https://github.com/redhat-cop/openshift-applier) 角色来自动化我的实现。您可能会发现这些工具很有价值，或者您可能会发现其他一些对您和组织更有效的工具。无论哪种方式，您都将发现自动化显著地减少了重新创建 CI/CD 组件所需的工作量。

#### 配置 Jenkins 主服务器

除了一般的“自动化”之外，我想单独介绍一下 Jenkins 主服务器，并讨论管理员如何利用 OpenShift 来自动化配置 Jenkins。来自 [Red Hat Container Catalog](https://access.redhat.com/containers/?tab=overview#/registry.access.redhat.com/openshift3/jenkins-2-rhel7) 的 Jenkins 镜像已经安装了 [OpenShift-Sync plugin](https://github.com/openshift/jenkins-sync-plugin)。在 [该视频](https://www.youtube.com/watch?v=zlL7AFWqzfw) 中，我们将讨论如何使用这个插件来创建 Jenkins 管道和从设备。

要创建 Jenkins 管道，请创建一个类似于下面的 OpenShift BuildConfig：

```
apiVersion: v1
kind: BuildConfig
...
spec:
  source:
    git:
      ref: master
      uri: <repository-uri>
  ...
  strategy:
    jenkinsPipelineStrategy:
      jenkinsfilePath: Jenkinsfile
    type: JenkinsPipeline
```

OpenShift-Sync 插件将注意到已经创建了带有 `jenkinsPipelineStrategy` 策略的 BuildConfig，并将其转换为 Jenkins 管道，从 Git 源指定的 Jenkinsfile 中提取。也可以使用内联 Jenkinsfile，而不是从 Git 存储库中提取。有关更多信息，请参阅[文档](https://docs.openshift.com/container-platform/3.11/dev_guide/dev_tutorials/openshift_pipeline.html#the-pipeline-build-config)。

要创建 Jenkins 从站，请创建一个 OpenShift ImageStream，它从以下定义开始：

```
apiVersion: v1
kind: ImageStream
metadata:
  annotations:
    slave-label: jenkins-slave
    labels:
      role: jenkins-slave
...
```

注意在这个 ImageStream 中定义的元数据。OpenShift-Sync 插件将把带有标签 `role: jenkins-slave` 的任何 ImageStream 转换为 Jenkins 从站。Jenkins 从站将以 `slave-label` 注释中的值命名。

ImageStreams 对于简单的 Jenkins 从属配置工作得很好，但是一些团队会发现有必要配置一些细节详情，比如资源限制、准备就绪和活动性探测，以及实例上限。这就是 ConfigMap 发挥作用的地方：

```
apiVersion: v1
kind: ConfigMap
metadata:
  labels:
  role: jenkins-slave
...
data:
  template1: |-
    <Kubernetes pod template>
```

注意，仍然需要角色：`jenkins-slave` 标签来将 ConfigMap 转换为 Jenkins 从站。Kubernetes pod 模板由一长段 XML 组成，它将根据组织的喜好配置每个细节。要查看此 XML，以及有关将 ImageStreams 和 ConfigMaps 转换为 Jenkins 从站的更多信息，请参阅[文档](https://docs.openshift.com/container-platform/3.11/using_images/other_images/jenkins.html#configuring-the-jenkins-kubernetes-plug-in)。

请注意上面所示的三个示例，其中没有一个操作需要管理员对 Jenkins 控制台进行手动更改。通过使用 OpenShift 资源，可以简单的自动化方式配置 Jenkins。

### 2、分享就是关爱

第二个最佳实践是维护一个公共 CI/CD 工件的 Git 存储库。主要思想是防止团队重新发明轮子。假设您的团队需要执行到 OpenShift 环境的蓝/绿部署，作为管道 CD 阶段的一部分。负责编写管道的团队成员可能不是 OpenShift 专家，也不可能具有从头开始编写此功能的能力。幸运的是，有人已经编写了一个将此功能合并到一个公共 CI/CD 存储库中的函数，因此您的团队可以使用该函数而不是花时间编写一个函数。

为了更进一步，您的组织可能决定维护整个管道。您可能会发现团队正在编写具有相似功能的管道。对于那些团队来说，使用来自公共存储库的参数化管道要比从头开始编写自己的管道更有效。

### 3、少即是多

正如我在前一节中提到的，第三个也是最后一个最佳实践是参数化您的 CI/CD 管道。参数化将防止过多的管道，使您的 CI/CD 系统更容易维护。假设我有多个区域可以部署应用程序。如果没有参数化，我需要为每个区域设置单独的管道。

要参数化一个作为 OpenShift 构建配置编写的管道，请将 `env` 节添加到配置：

```
...
spec:
  ...
  strategy:
    jenkinsPipelineStrategy:
      env:
      - name: REGION
        value: US-West
      jenkinsfilePath: Jenkinsfile
    type: JenkinsPipeline
```

使用此配置，我可以传递 `REGION` 参数给管道以将我的应用程序部署到指定区域。

这有一个[视频](https://www.youtube.com/watch?v=zlL7AFWqzfw)提供了一个更实质性的情况，其中参数化是必须的。一些组织决定把他们的 CI/CD 管道分割成单独的 CI 和 CD 管道，通常是因为在部署之前有一些审批过程。假设我有四个镜像和三个不同的环境要部署。如果没有参数化，我需要 12 个 CD 管道来允许所有部署可能性。这会很快失去控制。为了使 CD 流水线的维护更容易，组织会发现将镜像和环境参数化以便允许一个流水线执行多个流水线的工作会更好。

### 总结

企业级的 CI/CD 往往比许多组织预期的更加复杂。幸运的是，对于 Jenkins，有很多方法可以无缝地提供设置的自动化。维护一个公共 CI/CD 工件的 Git 存储库也会减轻工作量，因为团队可以从维护的依赖项中提取而不是从头开始编写自己的依赖项。最后，CI/CD 管道的参数化将减少必须维护的管道的数量。

如果您找到了其他不可或缺的做法，请在评论中分享。

---

via: <https://opensource.com/article/18/11/best-practices-cicd>

作者：[Austin Dewey](https://opensource.com/users/adewey) 选题：[lujun9972](https://github.com/lujun9972) 译者：[ChiZelin](https://github.com/ChiZelin) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


极客漫画：密室逃脱

How do I exit the Vim editor?

到目前为止，取得的成绩如下

Asked 7 years, 1 month ago

Active 26 days ago

Viewed 2.0m times

Asked 7 years, 8 months ago

Active yesterday

Viewed 2.1m times

Asked 10 years ago

Modified today

Viewed 2.7m times

Asked 11 years, 6 months ago

Modified 1 month ago

Viewed 3.0m times

![](/data/attachment/album/201812/22/095500lrn0rk05n9889cck.png)

“密室逃脱”是近年来流行的益智解密游戏。我们的主人公就进入了这样的一个游戏，然而，发现谜题是——如何退出 Vim！！！

“如何退出 Vim”这个话题，已经成了众多 Linux 初学者经常听说的一个梗了，比如说，“如何制造乱码？——新手退出 Vim”。

甚至在国外著名的问答网站 Stack Overflow 上，从 2012 年 8 月 6 日，jclancy 发了“[如何退出 Vim 编辑器](https://stackoverflow.com/questions/11828270/how-to-exit-the-vim-editor)”求救。6 年过去了，这个帖子已经有 170 万的阅读了。（2017 年 5 月 24 日，Stack Overflow 官方博客还专门发文纪念了该贴阅读量突破 100 百万次）

---

via: <http://turnoff.us/geek/escape-room/>

作者：[Daniel Stori](http://turnoff.us/about/) 译者 & 点评：[wxy](https://github.com/wxy) 校对 & 合成：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


如何在 Ubuntu 和其他 Linux 发行版上安装 Putty

感觉你说这些的时候肯定没有用过.ssh下的config

如果我没弄错，[Putty](https://www.putty.org/) 可能是 Windows 最受欢迎的 SSH 客户端。

在 IT 公司中，开发环境通常在远程 Linux 系统上，而开发人员则使用 Windows 作为本地系统。Putty 用于从 Windows 机器连接到远程 Linux 系统。

Putty 不是限定于 Windows 的。你也可以在 Linux 和 macOS 上使用此开源软件。

但是等等！当你已经拥有“真正的” Linux 终端时，为什么要在 Linux 上使用单独的 SSH 客户端？这有几个想在 Linux 上使用 Putty 的原因。

* 你在 Windows 上使用 Putty 已经很久了，你觉得它更舒服。
* 你发现很难手动编辑 SSH 配置文件以保存各种 SSH 会话。你更喜欢 Putty 图形化保存 SSH 连接的方式。
* 你想通过连接到原始套接字和串口进行调试。

无论是什么原因，如果你想在 Ubuntu 或任何其他 Linux 上使用 Putty，你当然可以这样做。让我告诉你如何做到。

### 在 Ubuntu Linux 上安装 Putty

![Installing Putty on Linux](/data/attachment/album/201812/22/181610el1fv5kt7lzee3bd.png)

对于 Ubuntu 用户来说，好消息是 Putty 可以在 Ubuntu 的 universe 仓库中找到。

要在 Ubuntu上安装 Putty，首先应确保已启用 universe 仓库。

```
sudo add-apt-repository universe
```

启用 universe 仓库后，应使用以下命令更新 Ubuntu：

```
sudo apt update
```

之后，你可以使用以下命令安装 Putty：

```
sudo apt install putty
```

安装后，你可以在菜单中找到它来启动 Putty。

正如你在下面的截图中看到的，Putty 的 Linux 版本看起来与 Windows 版本相同。这让你松了一口气, 因为你不必再尝试新的设置。

![Putty in Linux](/data/attachment/album/201812/22/181611vsvq4p4e49ev11dg.jpg)

当你输入远程系统的[主机名](https://itsfoss.com/change-hostname-ubuntu/)或 IP 地址并连接到它时，Putty 将使用你已保存在主目录中的 SSH 密钥。

![Using Putty in Ubuntu Linux](/data/attachment/album/201812/22/181612azmiibaq00sqhsbi.jpg)

### 在其他 Linux 发行版上安装 Putty

[Putty 可用于 Debian](https://packages.debian.org/jessie/putty)，所以你只需要使用 `apt-get` 或 `aptitude` 来安装它。

```
sudo apt-get install putty
```

Putty 也适用于 Fedora/Red Hat，并可以使用默认的包管理器进行安装。

```
sudo dnf install putty
```

你还可以在基于 Arch Linux 的发行版中轻松安装 Putty。

```
sudo pacman -S putty
```

请记住，Putty 是一款开源软件。如果你真的想要，你也可以通过源代码安装它。你可以从下面的链接获取 Putty 的源代码。

* [下载 Putty 源代码](https://www.chiark.greenend.org.uk/%7Esgtatham/putty/latest.html)

我一直喜欢原生 Linux 终端而不是像 Putty 这样的 SSH 客户端。我觉得 GNOME 终端或 [Terminator](https://launchpad.net/terminator) 更有家的感觉。但是，在 Linux 中使用默认终端或 Putty 是个人选择。

你在 Linux 上管理多个 SSH 连接时使用了什么？

---

via: <https://itsfoss.com/putty-linux/>

作者：[Abhishek Prakash](https://itsfoss.com/author/abhishek/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


Bash 环境变量的那些事

Summary: 初学者可以在此教程中了解环境变量。

初学者可以在此教程中了解环境变量。

>
> 初学者可以在此教程中了解环境变量。
>
>
>

![](/data/attachment/album/201812/23/151252vyyprdr6bdrioqgy.jpg)

bash 变量，尤其是讨厌的*环境变量*，已经是一个老生常谈的话题了。我们也更应该对它有一个详细的了解，让它为我们所用。

下面就打开终端，开始吧。

### 环境变量

`HOME` （LCTT 译注：双关语）除了是你脱下帽子惬意休息的地方，同时也是 Linux 中的一个变量，它是当前用户主目录的路径：

```
echo $HOME
```

以上这个命令会显示当前用户的主目录路径，通常都在 `/home/<your username>` 下。

顾名思义，变量的值是可以根据上下文变化的。实际上，Linux 系统中每一个用户的 `HOME` 变量都是不一样的，当然你也可以这样自行更改 `HOME` 变量的值：

```
HOME=/home/<your username>/Documents
```

以上这个命令将会把 `HOME` 变量设置为你的 `Documents` 目录。

其中有三点需要留意：

1. `=` 符号和其两侧的内容之间不加空格。空格在 shell 中有专门的意义，不能随意地在任何地方添加空格。
2. 如果你需要对变量进行赋值，只需要使用变量名称就可以了。但如果需要读取或者使用变量的值，需要在变量前面加上一个 `$` 号。
3. 更改 `HOME` 变量具有一定的风险。有很多程序是依赖于 `HOME` 变量的，更改 `HOME` 变量可能会导致一些不可预见的结果。例如，如果按照上面的方式更改了 `HOME` 变量，然后执行不带有任何参数的 `cd` 命令，在通常情况下，会跳转到用户的主目录下，但在这个时候，会跳转到 `HOME` 变量指定的目录下。

上面第 3 点中环境变量的更改并不是持久有效的，在终端关闭后重新打开终端，又或者是新建一个终端，执行 `echo $HOME` 命令输出的仍然会是初始的值，而不是重新自定义的值。

在讨论如何持久地更改一个环境变量之前，我们先来看一下另一个比较重要的环境变量。

### PATH 变量

在 `PATH` 变量中存放了一系列目录，而且是放置了可执行程序的目录。正是由于 `PATH` 变量的存在，让你不需要知道应用程序具体安装到了什么目录，而 shell 却可以正确地找到这些应用程序。

如果你查看 `PATH` 变量的值，大概会是以下这样：

```
$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin:/bin:/sbin
```

每两个目录之间使用冒号 `:` 分隔。如果某个应用程序的所在目录不在 `PATH` 变量中，那么运行的时候就需要声明应用程序的目录让 shell 能够找到。

```
/home/<user name>/bin/my_program.sh
```

例如以上命令就会执行当前用户 `bin/` 目录下的 `my_program.sh` 文件。

有一个常见的问题：如果你不希望弄乱系统的 `bin/` 目录，同时也不希望你自己的文件被其它人运行，还不想每次运行的时候都要输入完整的路径，那么，你可以在你的主目录中创建一个独立的 `bin/` 目录：

```
mkdir $HOME/bin
```

然后将这个目录添加到 `PATH` 变量中：

```
PATH=$PATH:$HOME/bin
```

然后 `/home/<user name>/bin/` 目录就会出现在 `PATH` 变量中了。但正如之前所说，这个变更只会在当前的 shell 生效，当前的 shell 一旦关闭，环境变量的值就又恢复原状了。

如果要让变更对当前用户持续生效，就不能在 shell 中直接执行对应的变更，而是应该将这些变更操作写在每次启动 shell 时都会运行的文件当中。这个文件就是当前用户主目录中的 `.bashrc` 文件。文件名前面的点号表明这是一个隐藏文件，执行普通的 `ls` 命令是不会将这个文件显示出来的，但只要在 `ls` 命令中加入 `-a` 参数就可以看到这个文件了。

你可以使用诸如 [kate](https://www.kde.org/applications/utilities/kate/)、[gedit](https://help.gnome.org/users/gedit/stable/)、[nano](https://www.nano-editor.org/) 或者 [vim](https://www.vim.org/) 这些文本编辑器来打开 `.bashrc` 文件（但不要用 LibreOffice Writer，它是一个文字处理软件，跟前面几个文字编辑器完全不同）。打开 `.bashrc` 文件之后，你会看见里面放置了一些 shell 命令，是用于为当前用户设置环境的。

在文件的末尾添加新行并输入以下内容：

```
export PATH=$PATH:$HOME/bin
```

保存并关闭 `.bashrc` 文件，接下来你就会看到 `export` 语句的效果。执行以下的命令让刚才的修改立即生效：

```
source .bashrc
```

刚才执行的 `source` 命令让 `.bashrc` 文件在当前的 shell 立即生效，并且对于之后打开的 shell 都会有效。因此另一个等效的方法是退出并重新进入 shell，但这样也太麻烦了。

现在，你的 shell 就能自动寻找到 `/home/<user name>/bin/` 下的程序了，执行这个目录下的程序也不需要完整地写出程序的路径。

### 自定义变量

当然，你也可以定义自己的变量。刚才我们看到的变量名称都是全大写的，实际上[变量名称的定义还是比较灵活的](https://bash.cyberciti.biz/guide/Rules_for_Naming_variable_name)。

定义新变量的过程非常直观，直接对它赋值就可以了：

```
new_variable="Hello"
```

然后可以用以下的方式读取到已定义变量的值：

```
echo $new_variable
```

程序的正常工作离不开各种变量，例如要将某个选项设置为打开，又或者让程序找到所需的代码库，都需要使用变量。在 bash 中运行程序的时候会生成一个子 shell，这个子 shell 和执行原程序的父 shell 并不是完全一样的，只是继承了父 shell 的部分内容，而且默认是不继承父 shell 中的变量的。因为变量默认情况下是局部变量，出于安全原因，一个 shell 中的局部变量不会被另一个 shell 读取到，即使是子 shell 也不可以。

下面举一个例子。首先定义一个变量：

```
robots="R2D2 & C3PO"
```

然后执行：

```
bash
```

现在是在 bash shell 中创建了一个子 shell。

执行这个命令看看还能不能读取到刚才定义的变量：

```
echo $robots
```

你会发现读取不到。

还是在这个子 shell 中，为 `robots` 变量赋一个不同的值：

```
robots="These aren't the ones you are looking for"
```

再读取一次：

```
$ echo $robots
These aren't the ones you are looking for
```

退出这个子 shell：

```
exit
```

然后再看一下现在 `robots` 变量的值：

```
$ echo $robots
R2D2 & C3P0
```

这一个特性可以有效避免配置过程中产生混乱，同时也会导致一个问题：如果程序中需要设置变量，但却由于子 shell 的原因无法正常访问到这个变量，该如何解决呢？这个时候就需要用到 `export` 了。

重复一次刚才的过程，但这一次不是通过 `robots="R2D2 & C3PO"` 方式来设置变量，而是使用 `export` 命令：

```
export robots="R2D2 & C3PO"
```

现在你会发现，在进入子 shell 之后，`robots` 变量的值仍然是最初赋予的值。

要注意的是，尽管子 shell 会继承通过 `export` 导出的变量，但如果在子 shell 中对这个变量重新赋值，是不会影响到父 shell 中对应变量的。

如果要查看所有通过 `export` 导出的变量，可以执行以下命令：

```
export -p
```

自定义的变量会显示在这个列表的末尾。这个列表中还有一些常见的变量：例如 `USER` 的值是当前用户的用户名，`PWD` 的值是当前用户当前所在的目录，而 `OLDPWD` 的值则是当前用户上一个访问过的目录。因此如果执行：

```
cd -
```

就会切换到上一个访问过的目录，那是因为 `cd` 命令读取到了 `OLDPWD` 变量的值。

你也可以使用 `env` 命令查看所有环境变量。

如果要取消导出一个变量，可以加上 `-n` 参数：

```
export -n robots
```

### 接下来

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

---

via: <https://www.linux.com/blog/learn/2018/12/bash-variables-environmental-and-otherwise>

作者：[Paul Brown](https://www.linux.com/users/bro66) 选题：[lujun9972](https://github.com/lujun9972) 译者：[HankChow](https://github.com/HankChow) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


树莓派在办公室的 11 种用法

![](/data/attachment/album/201812/23/153615u7gylclg77ospl3s.jpg)

我知道你在想什么：树莓派只能用在修修补补、原型设计和个人爱好中。它实际不能用在业务中。

毫无疑问，这台电脑的处理能力相对较低、易损坏的 SD 卡、缺乏电池备份以及支持的 DIY 性质，这意味着它不会是一个能在任何时候执行最关键的操作的[专业的、已安装好、配置好的商业服务器](https://dxmtechsupport.com.au/server-configuration)的可行替代品。

但是它电路板便宜、功耗很小、小到几乎适合任何地方、无限灵活 —— 这实际上是处理办公室一些基本任务的好方法。

而且，更好的是，已经有一些人完成了这些项目并很乐意分享他们是如何做到的。

### DNS 服务器

每次在浏览器中输入网站地址或者点击链接时，都需要将域名转换为数字 IP 地址，然后才能显示内容。

通常这意味着向互联网上某处 DNS 服务器发出请求 —— 但你可以通过本地处理来加快浏览速度。

你还可以分配自己的子域，以便本地访问办公室中的计算机。

[这里了解它是如何工作的。](https://www.1and1.com/digitalguide/server/configuration/how-to-make-your-raspberry-pi-into-a-dns-server/)

### 厕所占用标志

在厕所排过队吗？

这对于那些等待的人来说很烦人，花在处理它上面的时间会耗费你在办公室的工作效率。

我想你希望在办公室里也悬挂飞机上那个厕所有人的标志。

[Occu-pi](https://blog.usejournal.com/occu-pi-the-bathroom-of-the-future-ed69b84e21d5) 是一个非常简单的解决方案，使用磁性开关和树莓派来判断螺栓何时关闭，并在 Slack 频道中更新“厕所在使用中” —— 这意味着整个办公室的人都可以看一眼电脑或者移动设备知道是否有空闲的隔间。

### 针对黑客的蜜罐陷阱

黑客破坏了网络的第一个线索是一些事情变得糟糕，这应该会吓到大多数企业主。

这就是可以用到蜜罐的地方：一台没有任何服务的计算机位于你的网络，将特定端口打开，伪装成黑客喜欢的目标。

安全研究人员经常在网络外部部署蜜罐，以收集攻击者正在做的事情的数据。

但对于普通的小型企业来说，这些作为一种绊脚石部署在内部更有用。因为普通用户没有真正的理由想要连接到蜜罐，所以任何发生的登录尝试都是正在进行捣乱的非常好的指示。

这可以提供对外部人员入侵的预警，并且也可以提供对值得信赖的内部人员的预警。

在较大的客户端/服务器网络中，将它作为虚拟机运行可能更为实用。但是在无线路由器上运行的点对点的小型办公室/家庭办公网络中，[HoneyPi](https://trustfoundry.net/honeypi-easy-honeypot-raspberry-pi/) 之类的东西是一个很小的防盗报警器。

### 打印服务器

联网打印机更方便。

但更换所有打印机可能会很昂贵 —— 特别是如果你对现有的打印机感到满意的话。

[将树莓派设置为打印服务器](https://opensource.com/article/18/3/print-server-raspberry-pi)可能会更有意义。

### 网络附加存储（NAS）

将硬盘变为 NAS 是树莓派最早的实际应用之一，并且它仍然是最好的之一。

[这是如何使用树莓派创建 NAS。](https://howtoraspberrypi.com/create-a-nas-with-your-raspberry-pi-and-samba/)

### 工单服务器

想要在预算不足的情况下在服务台中支持工单？

有一个名为 osTicket 的完全开源的工单程序，它可以安装在你的树莓派上，它甚至还有[随时可用的 SD 卡镜像](https://everyday-tech.com/a-raspberry-pi-ticketing-system-image-with-osticket/)。

### 数字标牌

无论是用于活动、广告、菜单还是其他任何东西，许多企业都需要一种显示数字标牌的方式 —— 而树莓派的廉价和省电使其成为一个非常有吸引力的选择。

[这有很多可供选择的选项。](https://blog.capterra.com/7-free-and-open-source-digital-signage-software-options-for-your-next-event/)

### 目录和信息亭

[FullPageOS](https://github.com/guysoft/FullPageOS) 是一个基于 Raspbian 的 Linux 发行版，它直接引导到 Chromium 的全屏版本 —— 这非常适合导购、图书馆目录等。

### 基本的内联网 Web 服务器

对于托管一个面向公众的网站，你最好有一个托管帐户。树莓派不适合面对真正的网络流量。

但对于小型办公室，它可以托管内部业务维基或基本的公司内网。它还可以用作沙箱环境，用于试验代码和服务器配置。

[这里是如何在树莓派上运行 Apache、MySQL 和 PHP。](https://maker.pro/raspberry-pi/projects/raspberry-pi-web-server)

### 渗透测试器

Kali Linux 是专为探测网络安全漏洞而构建的操作系统。通过将其安装在树莓派上，你就拥有了一个超便携式穿透测试器，其中包含 600 多种工具。

[你可以在这里找到树莓派镜像的种子链接。](https://www.offensive-security.com/kali-linux-arm-images/)

绝对要小心只在你自己的网络或你有权对它安全审计的网络中使用它 —— 使用此方法来破解其他网络是严重的犯罪行为。

### VPN 服务器

当你外出时，依靠的是公共无线互联网，你无法控制还有谁在网络中、谁在窥探你的所有流量。这就是为什么通过 VPN 连接加密所有内容可以让人放心。

你可以订阅任意数量的商业 VPN 服务，并且你可以在云中安装自己的服务，但是在办公室运行一个 VPN，这样你也可以从任何地方访问本地网络。

对于轻度使用 —— 比如偶尔的商务旅行 —— 树莓派是一种强大的，节约能源的设置 VPN 服务器的方式。（首先要检查一下你的路由器是不是不支持这个功能，许多路由器是支持的。）

[这是如何在树莓派上安装 OpenVPN。](https://medium.freecodecamp.org/running-your-own-openvpn-server-on-a-raspberry-pi-8b78043ccdea)

### 无线咖啡机

啊，美味：好喝的饮料是神赐之物，也是公司内工作效率的支柱。

那么，为什么不[将办公室的咖啡机变成可以精确控制温度和无线连接的智能咖啡机呢？](https://www.techradar.com/au/how-to/how-to-build-your-own-smart-coffee-machine)

---

via: <https://blog.dxmtechsupport.com.au/11-uses-for-a-raspberry-pi-around-the-office/>

作者：[James Mawson](https://blog.dxmtechsupport.com.au/author/james-mawson/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出