第 9 章 系统技巧

# 第 9 章 系统技巧

这里，描述配置和管理系统的基本技巧，大部分在控制台操作。

## 9.1. 控制台技巧

有一些工具程序来帮助你的控制台活动。

表 9.1. 支持控制台活动的程序列表

软件包 | 流行度 | 大小 | 说明
--- | --- | --- | ---
`mc` | V:39, I:179 | 1590 | 参见 第 1.3 节 “Midnight Commander (MC)”
`bsdutils` | V:445, I:1000 | 338 | `script` (1) 命令来记录终端会话
`screen` | V:52, I:198 | 991 | VT100/ANSI 终端模拟器混合复用的终端
`tmux` | V:100, I:164 | 1391 | 终端复用的备选方案（使用 “Control-B”代替）
`ripgrep` | V:15, I:46 | 5284 | 在源代码树中快速递归搜索字符串，并自动过滤
`fzf` | V:8, I:34 | 5064 | 模糊的文本查找器
`fzy` | V:0.07, I:0.38 | 59 | 模糊的文本查找器
`rlwrap` | V:1, I:11 | 328 | 具备 readline 特征的命令行封装
`ledit` | V:0.4, I:7.8 | 375 | 具备 readline 特征的命令行封装
`rlfe` | V:0.02, I:0.50 | 45 | 具备 readline 特征的命令行封装

### 9.1.1. 清晰的记录 shell 活动

简单地使用 script(1) （参见 第 1.4.9 节 “记录 shell 活动”）记录 shell 活动会产生一个有控制字符的文件。这些控制字符可以按下面的方式，使用 col(1) 去掉。

    $ script
    Script started, file is typescript

做些操作……按 `Ctrl-D` 退出 `script`.

    $ col -bx < typescript > cleanedfile
    $ vim cleanedfile

有替代的方式来记录 shell 活动：

- 使用 `tee` (在 initramfs 的启动过程中可用)：

      $ sh -i 2>&1 | tee typescript

- 使用 `gnome-terminal` 增加行缓冲，用滚动条查看。

- 使用 `screen` 和 "`^A H`" (参见 第 9.1.2 节 “screen 程序”)来进行控制台记录。

- 使用 `vim` 输入 "`:terminal`" 进入终端模式。使用 "`Ctrl-W N`" 从终端模式退出到普通模式。使用 "`:w typescript`" 将缓存写到一个文件。

- 使用 `emacs` 和 "`M-x shell`", "`M-x eshell`", 或 "`M-x term`" 来进入记录控制台。使用 "`C-x C-w`" 将缓存写到文件。

### 9.1.2. screen 程序

screen(1) 不但允许一个终端窗口运行多个进程，还允许远程 shell 进程支持中断的连接.这里是一个典型的 screen(1) 使用场景.

1. 登录到一个远程机器。

2. 在单个控制台上启动 `screen`。

3. 使用 `^A c` ("Control-A" 接着 "c")在 `screen` 中创建的窗口执行多个程序.

4. 按 `^A n` ("Control-A" 接着"n")来在多个 `screen` 窗口间转换.

5. 突然，你需要离开你的终端，但你不想丢掉正在做的工作，而这些工作需要连接来保持。

6. 你可以通过任何方式分离 `screen` 会话。

   - 残忍地拔掉你的网络连接

   - 输入 `^A d` ("Control-A" 接着 "d") 并手工从远程连接退出登录

   - 输入 `^A DD` ("Control-A" 接着 "DD") 分离 `screen` 并退出登录

7. 你重新登录到同一个远处主机（即使从不同的终端）。

8. 使用 "`screen -r`" 启动 `screen`.

9. `screen` 魔术般的重新附上先前所有的 `screen` 窗口和所有在活动运行的程序.

| 提示
对于拨号或者按包计费的网络连接，你可以通过 `screen` 节省连接费用，应为你可以在断开连接时让一个进程继续运行，当你稍后再次连接时重新附上它。

在 `screen` 会话里,除了命令按键外的所有键盘输入都会被发送到当前窗口。 `screen` 所有命令按键是通过 `^A` ("Control-A") 加单个键[加任何参数] 来输入.这里有一些重要的命令按键需要记住。

表 9.2. screen 键绑定列表

键绑定功能 | 说明
--- | ---
`^A ?` | 显示帮助屏幕（显示键绑定）
`^A c` | 创建一个新的窗口并切换到该窗口
`^A n` | 到下一个窗口
`^A p` | 到前一个窗口
`^A 0` | 到 0 号窗口
`^A 1` | 到 1 号窗口
`^A w` | 显示窗口列表
`^A a` | 作为键盘输入发送 Ctrl-A 到当前窗口
`^A h` | 把当前窗口的硬拷贝写到一个文件
`^A H` | 开始/结束 当前窗口到文件的记录
`^A ^X` | 锁定终端(密码保护)
`^A d` | 从终端分离 screen 会话
`^A DD` | 分离 screen 会话并退出登录

细节参见 screen(1).

参见 tmux(1) ，了解替代命令的功能。

### 9.1.3. 在目录间游走

在 第 1.4.2 节 “定制 bash”， 2 个技巧允许快速在目录间游走，在 `$CDPATH` 和 `mc` 描述。

如果你使用模糊文本过滤程序，你能够不输入精准路径。对于`fzf` 软件包，在 `~/.bashrc` 里面包括下列内容。

    FZF_KEYBINDINGS_PATH=/usr/share/doc/fzf/examples/key-bindings.bash
    if [ -f $FZF_KEYBINDINGS_PATH ]; then
      . $FZF_KEYBINDINGS_PATH
    fi

例如：

- 你能够最小化的操作跳入非常深的子目录。你首先输入 "`cd **`" 后按 `Tab`。然后你将被提示候选路径。输入部分路径字符串，比如 `s/d/b foo`，将会缩窄候选路径。通过有光标和回车键的 `cd`，你选择将要使用的路径。

- 你可以用最小化的操作，从命令历史里面选择一个命令。在命令行提示符下按 `Ctrl-R` 。然后你将被提示候选的命令。输入部分命令字符串，比如 `vim d`，将会缩窄候选项。使用光标和回车键选择将要使用的命令。

### 9.1.4. Readline 封装

一些命令，比如 `/usr/bin/dash`，它缺少命令行历史编辑能力，但在 `rlwrap` 或它的等价物下运行就能够透明的增加这样的功能。

     $ rlwrap dash -i

这提供一个便利平台来测试 `dash` 的细微之处，使用类似 `bash` 的友好环境。

### 9.1.5. 扫描源代码树

在 `ripgrep` 软件包中的 rg(1) 命令，在扫描源代码树的典型场景中，提供了一个比 grep(1) 命令更快速的替代。它充分利用了现代多核 CPU，并自动使用适当的过滤器来忽略一些文件。

## 9.2. 定制 vim

在你通过 第 1.4.8 节 “使用 vim” 学习基本的vim(1) 后,请阅读 Bram Moolenaar 的"Seven habits of effective text editing (2000)" 来理解 `vim` 应当怎样被使用。

### 9.2.1. 用内部特性定制 vim

`vim` 的行为能够被显著的改变，通过 `Ex`-模式 的命令，启用它的内部特性，比如 "`set ...`" 来设置 vim 选项。

这些 `Ex`-模式 的命令，能够在用户的 vimrc 文件里面包括，传统的 "`~/.vimrc`" 或 git 友好的 "`~/.vim/vimrc`"。这里有一个非常简单的例子[2]：

    """ Generic baseline Vim and Neovim configuration (~/.vimrc)
    """   - For NeoVim, use "nvim -u ~/.vimrc [filename]"
    """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    let mapleader = ' '             " :h mapleader
    """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    set nocompatible                " :h 'cp -- sensible (n)vim mode
    syntax on                       " :h :syn-on
    filetype plugin indent on       " :h :filetype-overview
    set encoding=utf-8              " :h 'enc (default: latin1) -- sensible encoding
    """ current vim option value can be verified by :set encoding?
    set backspace=indent,eol,start  " :h 'bs (default: nobs) -- sensible BS
    set statusline=%<%f%m%r%h%w%=%y[U+%04B]%2l/%2L=%P,%2c%V
    set listchars=eol:¶,tab:⇄\ ,extends:↦,precedes:↤,nbsp:␣
    set viminfo=!,'100,<5000,s100,h " :h 'vi -- bigger copy buffer etc.
    """ Pick "colorscheme" from blue darkblue default delek desert elflord evening
    """ habamax industry koehler lunaperche morning murphy pablo peachpuff quiet ron
    """ shine slate torte zellner
    colorscheme industry
    """ don't pick "colorscheme" as "default" which may kill SpellUnderline settings
    set scrolloff=5                 " :h 'scr -- show 5 lines around cursor
    set laststatus=2                " :h 'ls (default 1)  k
    """ boolean options can be unset by prefixing "no"
    set ignorecase                  " :h 'ic
    set smartcase                   " :h 'scs
    set autoindent                  " :h 'ai
    set smartindent                 " :h 'si
    set nowrap                      " :h 'wrap
    "set list                        " :h 'list (default nolist)
    set noerrorbells                " :h 'eb
    set novisualbell                " :h 'vb
    set t_vb=                       " :h 't_vb -- termcap visual bell
    set spell                       " :h 'spell
    set spelllang=en_us,cjk         " :h 'spl -- english spell, ignore CJK
    set clipboard=unnamedplus       " :h 'cb -- cut/copy/paste with other app
    set hidden                      " :h 'hid
    set autowrite                   " :h 'aw
    set timeoutlen=300              " :h 'tm

`vim` 的键盘映射能够在用户的 vimrc 文件里面修改。例如：

| 小心
没有非常好的理由，请不要尝试改变默认的键绑定。

    """ Popular mappings (imitating LazyVim etc.)
    """ Window moves without using CTRL-W which is dangerous in INSERT mode
    nnoremap <C-H> <C-W>h
    nnoremap <C-J> <C-W>j
    nnoremap <C-K> <C-W>k
    silent! nnoremap <C-L> <C-W>l
    """ Window resize
    nnoremap <C-LEFT> <CMD>vertical resize -2<CR>
    nnoremap <C-DOWN> <CMD>resize -2<CR>
    nnoremap <C-UP> <CMD>resize +2<CR>
    nnoremap <C-RIGHT> <CMD>vertical resize +2<CR>
    """ Clear hlsearch with <ESC> (<C-L> is mapped as above)
    nnoremap <ESC> <CMD>noh<CR><ESC>
    inoremap <ESC> <CMD>noh<CR><ESC>
    """ center after jump next
    nnoremap n nzz
    nnoremap N Nzz
    """ fast "jk" to get out of INSERT mode (<ESC>)
    inoremap  jk <CMD>noh<CR><ESC>
    """ fast "<ESC><ESC>" to get out of TERM mode (CTRL-\ CTRL-N)
    tnoremap <ESC><ESC> <C-\><C-N>
    """ fast "jk" to get out of TERM mode (CTRL-\ CTRL-N)
    tnoremap jk <C-\><C-N>
    """ previous/next trouble/quickfix item
    nnoremap [q <CMD>cprevious<CR>
    nnoremap ]q <CMD>cnext<CR>
    """ buffers
    nnoremap <S-H> <CMD>bprevious<CR>
    nnoremap <S-L> <CMD>bnext<CR>
    nnoremap [b <CMD>bprevious<CR>
    nnoremap ]b <CMD>bnext<CR>
    """ Add undo break-points
    inoremap  , ,<C-G>u
    inoremap  . .<C-G>u
    inoremap  ; ;<C-G>u
    """ save file
    inoremap <C-S> <CMD>w<CR><ESC>
    xnoremap <C-S> <CMD>w<CR><ESC>
    nnoremap <C-S> <CMD>w<CR><ESC>
    snoremap <C-S> <CMD>w<CR><ESC>
    """ better indenting
    vnoremap < <gv
    vnoremap > >gv
    """ terminal (Somehow under Linux, <C-/> becomes <C-_> in Vim)
    nnoremap <C-_> <CMD>terminal<CR>
    "nnoremap <C-/> <CMD>terminal<CR>
    """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    if ! has('nvim')
    """ Toggle paste mode with <SPACE>p for Vim (no need for Nvim)
    set pastetoggle=<leader>p
    """ nvim default mappings for Vim.  See :h default-mappings in nvim
    """ copy to EOL (no delete) like D for d
    noremap Y y$
    """ sets a new undo point before deleting
    inoremap <C-U> <C-G>u<C-U>
    inoremap <C-W> <C-G>u<C-W>
    """ <C-L> is re-purposed as above
    """ execute the previous macro recorded with Q
    nnoremap Q @@
    """ repeat last substitute and *KEEP* flags
    nnoremap & :&&<CR>
    """ search visual selected string for visual mode
    xnoremap * y/\V<C-R>"<CR>
    xnoremap # y?\V<C-R>"<CR>
    endif

为了使上面的按键绑定正确地运行，终端程序需要配置：`Backspace`-键产生 "ASCII DEL"、`Delete`-键产生 "Escape sequence"。

其它杂项配置能够在用户的 vimrc 文件里面修改。例如：

    """ Use faster 'rg' (ripgrep package) for :grep
    if executable("rg")
      set grepprg=rg\ --vimgrep\ --smart-case
      set grepformat=%f:%l:%c:%m
    endif
    """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    """ Retain last cursor position :h '"
    augroup RetainLastCursorPosition
      autocmd!
      autocmd BufReadPost *
        \ if line("'\"") > 0 && line ("'\"") <= line("$") |
        \   exe "normal! g'\"" |
        \ endif
    augroup END
    """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    """ Force to use underline for spell check results
    augroup SpellUnderline
      autocmd!
      autocmd ColorScheme * highlight SpellBad term=Underline gui=Undercurl
      autocmd ColorScheme * highlight SpellCap term=Underline gui=Undercurl
      autocmd ColorScheme * highlight SpellLocal term=Underline gui=Undercurl
      autocmd ColorScheme * highlight SpellRare term=Underline gui=Undercurl
    augroup END
    """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    """ highlight tailing spaces except when typing as red (set after colorscheme)
    highlight TailingWhitespaces ctermbg=red guibg=red
    """ \s\+     1 or more whitespace character: <Space> and <Tab>
    """ \%#\@<!  Matches with zero width if the cursor position does NOT match.
    match TailingWhitespaces /\s\+\%#\@<!$/

### 9.2.2. 用外部软件包定制 vim

下面能够发现有趣的外部插件软件包：

- Vim - 无所不在的文本编辑器 -- Vim 和 vim 脚本的官方上游站点

- VimAwsome -- Vim 插件列表

- vim-scripts -- Debian 软件包：一个 vim 脚本的收集

vim-scripts 软件包里面的插件软件包，能够使用用户的 vimrc 文件启用。比如：

    packadd! secure-modelines
    packadd! winmanager
    " IDE-like UI for files and buffers with <space>w
    nnoremap <leader>w         :WMToggle<CR>

新的原生 Vim 软件包系统同 "`git`" 和 "`git submodule`" 顺利的工作。一个这样的配置例子能够在 我的 git 仓库: dot-vim 找到。本质上是这样做的：

- 通过使用"`git`"和"`git submodule`"，最新的扩展软件包，比如说"`name`"，会被放到 `~/.vim/pack/*/opt/name` 和类似的地方。

- 通过增加 `:packadd! name` 行到用户的 vimrc 文件，这些软件包被放到 `runtimepath`。

- Vim 在它的初始化时加载这些软件包到 `runtimepath`。

- 在它初始化的最后，安装文档的标签被更新，使用 "`helptags ALL`"。

更多信息，请使用 "`vim --startuptime vimstart.log`" 启动 `vim` 来检查实际的执行顺序和每一个步骤消耗的时间。

是相当迷惑的看到这么多的方式[3] 来管理和加载这些外部的软件包到 `vim`。检查原始的信息是最好的方法。

表 9.3. `vim` 的初始化信息

按键 | 信息
--- | ---
`:help package` | 解释 vim 软件包机制
`:help runtimepath` | 解释 `runtimepath` 机制
`:version` | 内部状态，包括 vimrc 文件的候选
`:echo $VIM` | 环境变量 "`$VIM`" 用来定位 vimrc 文件的路径
`:set runtimepath?` | 列出用来搜索所有运行时支持文件的目录
`:echo $VIMRUNTIME` | 环境变量 "`$VIMRUNTIME`" 用来定位大量系统提供的运行时支持文件

## 9.3. 数据记录和展示

### 9.3.1. 日志后台守护进程（daemon）

许多传统的程序在"`/var/log/`" 目录下用文本文件格式记录它们的活动.

在一个产生很多日志文件的系统上，用 logrotate(8)来简化日志文件的管理。

许多新的程序使用 systemd-journald(8) 日志服务的二进制文件格式来记录它们的活动，在"`/var/log/journal`" 目录下。

你能够从 shell 脚本记录数据到 systemd-journald(8) 日志，使用 systemd-cat(1) 命令。

参见 第 3.5 节 “系统消息” 和 第 3.4 节 “内核消息”.

### 9.3.2. 日志分析

这里是主要的日志分析软件 ("`~Gsecurity::log-analyzer`" 在 aptitude(8) 中).

表 9.4. 系统日志分析软件列表

软件包 | 流行度 | 大小 | 说明
--- | --- | --- | ---
`fail2ban` | V:102, I:113 | 2191 | 禁用造成多个认证错误的 IP
`logwatch` | V:9, I:11 | 2440 | 用 Perl 写的日志分析软件，有好的输出
`awstats` | V:5.3, I:8.4 | 6934 | 强大和特性全面的 web 服务器日志分析
`analog` | V:3, I:84 | 3739 | web 服务器日志分析
`sarg` | V:0.71, I:0.79 | 863 | 生成 squid 分析报告
`pflogsumm` | V:1.5, I:3.7 | 173 | Postfix 日志条目概要
`fwlogwatch` | V:0.12, I:0.18 | 487 | 防火墙日志分析软件
`squidview` | V:0.02, I:0.43 | 224 | 监控和分析 squid access.log 文件
`swatch` | V:0.07, I:0.31 | 99 | 有正则表达式、高亮和曲线的日志文件查看器
`crm114` | V:0.06, I:0.33 | 1371 | Controllable Regex Mutilator 和垃圾邮件过滤 (CRM114)
`icmpinfo` | V:0.01, I:0.38 | 42 | 解释 ICMP 信息

| 注意
CRM114 提供语言架构来写模糊 过滤器，使用了 TRE 正则表达式库 。它主要在垃圾邮件过滤器中使用，但也能够用于日志分析。

### 9.3.3. 定制文本数据的显示

尽管例如 more(1) 和 less(1) 这样的分页程序（参见 第 1.4.5 节 “分页程序”）和用于高亮和格式的自定义工具（参见 第 11.1.8 节 “高亮并格式化纯文本数据”）可以漂亮地显示文本数据，但通用的编辑器 （参见 第 1.4.6 节 “文本编辑器”）是用途最广的，且可定制性最高。

| 提示
对于 vim(1) 和它的分页模式别名 view(1)，“`:set hls`” 可以启用高亮搜索。

### 9.3.4. 定制时间和日期的显示

“`ls -l`” 命令默认的时间和日期显示格式取决于语言环境（相关的值参见 第 1.2.6 节 “时间戳”）。“`$LANG`” 变量将被首先考虑，但它会被导出的 “`$LC_TIME`” 或 "`$LC_ALL`"环境变量覆盖。

每个语言环境实际的默认显示格式取决于所使用的 C 标准库的版本（`libc6` 软件包），也就是说，不同的 Debian 发行版有不同的默认情况。对于 iso-formates，参见 ISO 8601。

如果你真的想自定义超出语言环境的时间和日期显示格式，你应该通过 “`--time-style`” 参数或 “`$TIME_STYLE`” 的值来设置时间样式值（参见ls(1)、date(1)、“`info coreutils 'ls invocation'`”）。

表 9.5. 使用 时间样式值 的"`ls -l`" 命令的时间和日期的显示例子

时间样式值 | 语言环境 | 时间和日期显示
--- | --- | ---
`iso` | 任何值 | `01-19 00:15`
`long-iso` | 任何值 | `2009-01-19 00:15`
`full-iso` | 任何值 | `2009-01-19 00:15:16.000000000 +0900`
`语言环境` | `C` | `Jan 19 00:15`
`语言环境` | `en_US.UTF-8` | `Jan 19 00:15`
`语言环境` | `es_ES.UTF-8` | `ene 19 00:15`
`+%d.%m.%y %H:%M` | 任何值 | `19.01.09 00:15`
`+%d.%b.%y %H:%M` | `C` 或 `en_US.UTF-8` | `19.Jan.09 00:15`
`+%d.%b.%y %H:%M` | `es_ES.UTF-8` | `19.ene.09 00:15`

| 提示

你可以使用命令别名以避免在命令行中输入长的选项，（参见 第 1.5.9 节 “命令别名”）：

    alias ls='ls --time-style=+%d.%m.%y %H:%M'

### 9.3.5. shell 中 echo 的颜色

大部分现代终端的 shell 中 echo 能够使用 ANSI 转义字符来显示颜色（参见 “`/usr/share/doc/xterm/ctlseqs.txt.gz`”）。

尝试下列例子

    $ RED=$(printf "\x1b[31m")
    $ NORMAL=$(printf "\x1b[0m")
    $ REVERSE=$(printf "\x1b[7m")
    $ echo "${RED}RED-TEXT${NORMAL} ${REVERSE}REVERSE-TEXT${NORMAL}"

### 9.3.6. 有颜色输出的命令

在交互式的环境下，命令的输出带颜色，能够给检查命令的输出带来便利。 我在我的"`~/.bashrc`"里加入了下面内容.

    if [ "$TERM" != "dumb" ]; then
        eval "`dircolors -b`"
        alias ls='ls --color=always'
        alias ll='ls --color=always -l'
        alias la='ls --color=always -A'
        alias less='less -R'
        alias ls='ls --color=always'
        alias grep='grep --color=always'
        alias egrep='egrep --color=always'
        alias fgrep='fgrep --color=always'
        alias zgrep='zgrep --color=always'
    else
        alias ll='ls -l'
        alias la='ls -A'
    fi

在交互式命令中，使用别名来限制颜色的影响范围。导出环境变量 "`export GREP_OPTIONS='--color=auto'`" 也有好处，这样能够让 less(1) 之类的页面程序看到颜色。当使用管道到其它命令时，你想去掉颜色，上面列子 "`~/.bashrc`" 中的内容，可以使用 "`--color=auto`" 代替.

| 提示
在交互式的环境中，通过"`TERM=dumb bash`"调用 shell ，你能够关闭这些颜色别名。

### 9.3.7. 记录编辑器复杂的重复操作动作

你能够记录编辑器复杂的重复操作动作。

对于 Vim,请按下面操作。

- "`qa`": 开始记录输入字符到有名字的寄存器 "`a`".

- … 编辑器操作

- "`q`": 结束记录输入的字符。

- "`@a`":执行寄存器 "`a` 的内容".

对于 Emacs, 请按下面操作。

- "`C-x (`": 开始定义一个键盘宏.

- … 编辑器操作

- "`C-x )`":结束定义一个键盘宏.

- "`C-x e`": 执行一个键盘宏.

### 9.3.8. 记录 X 应用程序的图像

有少量方法可以记录 X 应用程序的图像，包括 `xterm` 显示。

表 9.6. 图形图像处理工具列表

软件包 | 流行度 | 大小 | 屏幕 | 命令
--- | --- | --- | --- | ---
`gnome-screenshot` | V:12, I:99 | 1115 | Wayland | GNOME 下的截屏程序
`flameshot` | V:7, I:16 | 3542 | Wayland | 强大的截屏程序
`gimp` | V:41, I:214 | 32779 | Wayland + X | GUI 菜单中的截屏
`x11-apps` | V:30, I:440 | 2461 | X | xwd(1)
`imagemagick` | V:7, I:275 | 81 | X | import(1)
`scrot` | V:4, I:49 | 144 | X | scrot(1)

### 9.3.9. 记录配置文件的变更

有特定的工具可以通过 DVCS 的帮助来记录配置文件的变更和在 Btrfs 上制作系统快照。

表 9.7. 记录配置历史的软件包列表

软件包 | 流行度 | 大小 | 说明
--- | --- | --- | ---
`etckeeper` | V:23, I:26 | 157 | 使用 Git（默认）、Mercurial 或 GNU Bazaar 来保存配置文件和它们的元数据
`timeshift` | V:8, I:14 | 4481 | 使用 rsync 或 BTRFS 快照的系统恢复工具
`snapper` | V:6.3, I:8.4 | 2396 | Linux 文件系统快照管理工具

你也可以考虑本地脚本 第 10.2.3 节 “备份技巧” 方案。

## 9.4. 监控、控制和启动程序活动

程序活动能够使用特殊的工具监控和控制。

表 9.8. 监控和控制程序活动工具列表

软件包 | 流行度 | 大小 | 说明
--- | --- | --- | ---
`coreutils` | V:910, I:1000 | 17994 | nice(1): 用指定的调度优先权运行一个程序
`bsdutils` | V:445, I:1000 | 338 | renice(1): 调整一个目前在运行的进程的调度优先权值
`procps` | V:828, I:998 | 2691 | "`/proc`" 文件系统工具: ps(1), top(1), kill(1), watch(1), …
`psmisc` | V:400, I:720 | 950 | "`/proc`" 文件系统工具: killall(1), fuser(1), peekfd(1), pstree(1)
`time` | V:6, I:79 | 148 | time(1):运行一个程序，并从时间消耗方面来报告系统资源的使用
`sysstat` | V:118, I:168 | 1904 | sar(1), iostat(1), mpstat(1), …: linux 系统性能工具
`isag` | V:0.1, I:3.3 | 109 | sysstat 的交互式的系统活动图
`lsof` | V:449, I:949 | 492 | lsof(8): 使用 "`-p`" 选项列出被一个系统进程打开的文件
`strace` | V:11, I:103 | 4393 | strace(1):跟踪系统调用和信号
`ltrace` | V:1, I:12 | 420 | ltrace(1): 跟踪库调用
`xtrace` | V:0.05, I:0.71 | 342 | xtrace(1):跟踪 X11 客户端和服务器端之间的通信
`powertop` | V:35, I:226 | 696 | powertop(1):系统能耗使用信息
`cron` | V:922, I:997 | 247 | 根据 cron(8) 后台守护进程（daemon）的调度运行一个进程
`anacron` | V:416, I:475 | 110 | 用于非整天 24 小时运行系统的命令计划，类 cron
`at` | V:72, I:96 | 158 | at(1) 或 batch(1): 在一个特定的时间运行任务或在某一系统负载下运行

| 提示
`procps` 包提供了非常基础的监控、控制程序活动功能和启动程序功能。你应当把他们全部学会。

### 9.4.1. 进程耗时

显示 命令调用进程的时间消耗。

    # time some_command >/dev/null
    real    0m0.035s       # time on wall clock (elapsed real time)
    user    0m0.000s       # time in user mode
    sys     0m0.020s       # time in kernel mode

### 9.4.2. 调度优先级

进程的调度优先级是被一个进程优先级值控制。

表 9.9. 调度优先级值列表

进程优先级值 | 调度优先级
--- | ---
19 | 最低优先级进程
0 | 非常高的普通用户优先级进程
-20 | root 用户非常高的优先级进程

    # nice  -19 top                                      # very nice
    # nice --20 wodim -v -eject speed=2 dev=0,0 disk.img # very fast

在某些情况下，极端的进程优先级值会对系统造成伤害。小心使用这个命令。

### 9.4.3. ps 命令

在 Debian 系统上的 ps(1) 命令同时支持 BSD 和 SystemV 特征，有助于识别静态的进程活动。

表 9.10. ps 命令样式列表

样式 | 典型的命令 | 特征
--- | --- | ---
BSD | `ps aux` | 显示 %CPU %MEM
System V | `ps -efH` | 显示 PPID

对于僵尸（死了的）子进程，你能够通过 "`PPID`" 字段的父进程 ID 来杀死它们。

pstree(1) 命令显示进程树。

### 9.4.4. top 命令

Debian 系统上的 top(1) 拥有丰富的特征，有助于识别进程有趣的动态行为。

它是一个交互式的全屏程序。你可以通过按"h"键来得到它的使用帮助，按"q"键来终止该程序。

### 9.4.5. 列出被一个进程打开的文件

你能够通过一个进程 ID(PID)来列出该进程所有打开的文件，例如，PID 为 1 的进程，使用下面的方式。

    $ sudo lsof -p 1

PID=1 通常用于 `init` 程序.

### 9.4.6. 跟踪程序活动

你能够跟踪程序活动，使用strace(1), ltrace(1), xtrace(1) 来跟踪系统调用和信号、库调用、X11 客户端和服务器端之间的通信。

跟踪 `ls` 命令的系统调用。

    $ sudo strace ls

| 提示
使用在 /usr/share/doc/strace/examples/ 中发现的 strace-graph 脚本来生成一个好看的树形视图

### 9.4.7. 识别使用文件和套接字的进程

你可以通过 fuser(1) 来识别出使用文件的进程，例如，用下面的方式识别出 "`/var/log/mail.log`" 由哪个进程打开。

    $ sudo fuser -v /var/log/mail.log
                         USER        PID ACCESS COMMAND
    /var/log/mail.log:   root       2946 F.... rsyslogd

你可以看到 "`/var/log/mail.log`" 是由 rsyslogd(8) 命令打开并写入。

你可以通过 fuser(1) 来识别出使用套接字的进程，例如，用下面的方式识别出 "`smtp/tcp`" 由哪个进程打开。

    $ sudo fuser -v smtp/tcp
                         USER        PID ACCESS COMMAND
    smtp/tcp:            Debian-exim   3379 F.... exim4

现在你知道你的系统运行 exim4(8) 来处理连接到 SMTP 端口 (25)的 TCP 连接.

### 9.4.8. 使用固定间隔重复一个命令

watch(1) 使用固定间隔重新执行一个命令，并全屏显示输出。

    $ watch w

显示哪些人登录到系统，每 2 秒钟更新一次。

### 9.4.9. 使用文件循环来重复一个命令

通过匹配某些条件的文件来循环重复一个命令，有几种方法，例如，匹配全局模式"`*.ext`".

- Shell 循环方式(参见 第 12.1.4 节 “shell 循环”):

    for x in *.ext; do if [ -f "$x"]; then command "$x" ; fi; done

- find(1) 和 xargs(1) 联合:

    find . -type f -maxdepth 1 -name '*.ext' -print0 | xargs -0 -n 1 command

- find(1) 使用 "`-exec`" 选项并执行命令:

    find . -type f -maxdepth 1 -name '*.ext' -exec command '{}' \;

- find(1) 使用 "`-exec`" 选项并执行一个短的 shell 脚本:

    find . -type f -maxdepth 1 -name '*.ext' -exec sh -c "command '{}' && echo 'successful'" \;

上面的列子确保适当处理怪异的文件名（如包含空格）。 find(1) 更多高级的用法，参见 第 10.1.5 节 “查找文件的语法”.

### 9.4.10. 从 GUI 启动一个程序

对于 命令行界面（command-line interface，CLI），`$PATH` 环境变量所指定的目录中第一个匹配相应名称的程序会被执行。参见 第 1.5.3 节 “"`$PATH`" 变量”。

对于遵从 freedesktop.org 标准的 图形用户界面（graphical user interface，GUI），`/usr/share/applications/` 目录中的 `*.desktop` 文件给每个程序的 GUI 菜单显示提供了必要的属性。遵从Freedesktop.org xdg 菜单系统的每一个软件包，通过 "/usr/share/applications/"下 "*.desktop"提供的数据来安装它的菜单。 遵从 Freedesktop.org 标准的现代桌面环境，用 xdg-utils 软件包利用这些数据生成它们的菜单。参见"/usr/share/doc/xdg-utils/README"。

举个例子，`chromium.desktop` 文件中为 “Chromium 网络浏览器” 定义了相关属性，例如程序名 “Name”，程序执行路径和参数 “Exec”，所使用的图标 “Icon” 等等（参见 桌面配置项规范）。文件内容如下：

    [Desktop Entry]
    Version=1.0
    Name=Chromium Web Browser
    GenericName=Web Browser
    Comment=Access the Internet
    Comment[fr]=Explorer le Web
    Exec=/usr/bin/chromium %U
    Terminal=false
    X-MultipleArgs=false
    Type=Application
    Icon=chromium
    Categories=Network;WebBrowser;
    MimeType=text/html;text/xml;application/xhtml_xml;x-scheme-handler/http;x-scheme-handler/https;
    StartupWMClass=Chromium
    StartupNotify=true

这是一个较为简单的说明。`*.desktop` 文件像下面那样被搜寻。

桌面环境设置 `$XDG_DATA_HOME` 和 `$XDG_DATA_DIR` 环境变量。举个例子，在 GNOME 中：

- 未设置 `$XDG_DATA_HOME`。（将使用默认值 `$HOME/.local/share`。）

- `$XDG_DATA_DIRS` 被设置为 `/usr/share/gnome:/usr/local/share/:/usr/share/`。

基准目录（参见 XDG Base Directory Specification）和`应用程序`目录如下所示。

- `$HOME/.local/share/` → `$HOME/.local/share/applications/`

- `/usr/share/gnome/` → `/usr/share/gnome/applications/`

- `/usr/local/share/` → `/usr/local/share/applications/`

- `/usr/share/` → `/usr/share/applications/`

`*.desktop` 文件将按照这个顺序在这些 `applications` 目录中进行搜寻。

| 提示
要建立一个用户自定义的 GUI 菜单项，需要在 `$HOME/.local/share/applications/` 目录中添加一个 `*.desktop` 文件。

| 提示
"`Exec=...`" 行不会由 shell 解析。如果需要设置环境变量，使用 env(1) command。

| 提示
相似地，如果在这些基准目录下的 `autostart` 目录中建立了一个 `*.desktop` 文件，则 `*.desktop` 文件中指定的程序会在桌面环境启动时自动执行。参见 Desktop Application Autostart Specification。

| 提示
相似地，如果在 `$HOME/Desktop` 目录中建立了一个 `*.desktop` 文件并且桌面环境被配置为支持桌面图标启动器功能，则点击图标时指定的程序会被执行。请注意，`$HOME/Desktop` 目录的实际名称与语言环境有关。参见 xdg-user-dirs-update(1)。

### 9.4.11. 自定义被启动的程序

一些程序会被另一个程序自动启动。下面是自定义该过程的方法。

- 应用程序配置菜单：

  - GNOME 桌面：“设置” → “应用程序” → “默认应用程序”

  - KDE 桌面：“应用开始菜单”→ “设置” → “系统设置” → “应用程序” → “默认应用程序”

  - Iceweasel 浏览器：“编辑” → “首选项” → “应用程序”

  - mc(1)：“`/etc/mc/mc.ext`”

- 例如 “`$BROWSER`”、“`$EDITOR`”、“`$VISUAL`” 和 “`$PAGER`” 这样的环境变量（参见 environ(7)）

- 用于例如 “`editor`”、“`view`”、“`x-www-browser`”、“`gnome-www-browser`” 和 “`www-browser`” 这样的程序的 update-alternatives(1) 系统（参见 第 1.4.7 节 “设置默认文本编辑器”）

- “`~/.mailcap`” 和 “`/etc/mailcap`” 文件的内容关联了程序的 MIME 类型（参见 mailcap(5)）

- “`~/.mime.types`” 和 “`/etc/mime.types`” 文件的内容关联了 MIME 类型的文件扩展名（参见 run-mailcap(1)）

| 提示
update-mime(8) 会更新 "`/etc/mailcap`" 文件，期间会用到 "`/etc/mailcap.order`" 文件 (参见 mailcap.order(5)).

| 提示
`debianutils` 软件包提供 sensible-browser(1)、sensible-editor(1) 和 sensible-pager(1)，它们可以分别对要调用的编辑器、分页程序和网络浏览器作出明智的选择。我建议你阅读那些 shell 脚本。

| 提示

为了在 GUI（图形用户界面）下运行例如 `mutt` 这样的控制台应用程序来作为你的首选应用程序，你应该像下面那样建立一个 GUI（图形用户界面）应用程序并设置 “`/usr/local/bin/mutt-term`” 为你想要启动的首选应用程序。

    # cat /usr/local/bin/mutt-term <<EOF
    #!/bin/sh
    gnome-terminal -e "mutt \$@"
    EOF
    # chmod 755 /usr/local/bin/mutt-term

### 9.4.12. 杀死一个进程

使用 kill(1) 通过进程 ID 来杀死（发送一个信号）一个进程。

使用 killall(1) 或 pkill(1) 通过进程命令的名字或其它属性来做同样的事情。

表 9.11. kill 命令常用信号列表

信号值 | 信号名 | 操作 | 注释
--- | --- | --- | ---
0 | --- | 没有信号发送(参见 kill(2)) | 检查进程是否运行
1 | SIGHUP | 终止进程 | 从终端断开连接（信号 挂起）
2 | SIGINT | 终止进程 | 从键盘中断 (`CTRL-C`)
3 | SIGQUIT | 终止进程并触发 dump core | 从键盘退出 (`CTRL-\`)
9 | SIGKILL | 终止进程 | 不可阻塞的 kill 信号
15 | SIGTERM | 终止进程 | 可被阻塞的终止信号

### 9.4.13. 单次任务时间安排

运行 at(1) 命令来安排一次性的工作。

    $ echo 'command -args'| at 3:40 monday

### 9.4.14. 定时任务安排

使用 cron(8) 来进行定时任务安排。参见 crontab(1) 和 crontab(5).

你能够作为一个普通用户定时运行一个进程，比如， `foo` 使用 "`crontab -e`" 命令创建一个 crontab(5) 的文件 "`/var/spool/cron/crontabs/foo`"。

这里是一个 crontab(5) 文件的列子。

    # use /usr/bin/sh to run commands, no matter what /etc/passwd says
    SHELL=/bin/sh
    # mail any output to paul, no matter whose crontab this is
    MAILTO=paul
    # Min Hour DayOfMonth Month DayOfWeek command (Day... are OR'ed)
    # run at 00:05, every day
    5  0  *  * *   $HOME/bin/daily.job >> $HOME/tmp/out 2>&1
    # run at 14:15 on the first of every month -- output mailed to paul
    15 14 1  * *   $HOME/bin/monthly
    # run at 22:00 on weekdays(1-5), annoy Joe. % for newline, last % for cc:
    0 22 *   * 1-5 mail -s "It's 10pm" joe%Joe,%%Where are your kids?%.%%
    23 */2 1 2 *   echo "run 23 minutes after 0am, 2am, 4am ..., on Feb 1"
    5  4 *   * sun echo "run at 04:05 every Sunday"
    # run at 03:40 on the first Monday of each month
    40 3 1-7 * *   [ "$(date +%a)" == "Mon" ] && command -args

| 提示
对那些非连续运行的系统，安装 `anacron` 软件包来定时执行周期性的命令，命令在接近机器启动的时间运行，并允许有特定的时间间隔。参见 anacron(8) 和 anacrontab(5).

| 提示
对于定时系统维护脚本，你能够以root 账户定时运行，把这类脚本放入 "`/etc/cron.hourly/`", "`/etc/cron.daily/`", "`/etc/cron.weekly/`", 或 "`/etc/cron.monthly/`". 这些脚本的执行时间，可以通过 "`/etc/crontab`" 和 "`/etc/anacrontab`" 来定制。

`cron` 后台守护进程（daemon）不存在时，Systemd 也有按时间计划运行程序的低级能力。例如， `/lib/systemd/system/apt-daily.timer` 和 `/lib/systemd/system/apt-daily.service` 建立每天的 apt 下载行动。参见 systemd.timer(5) 。

### 9.4.15. 基于事件的计划任务

Systemd 能够执行计划程序，不仅基于时间事件，还能够基于挂载事件。参见 第 10.2.3.3 节 “时间事件触发的备份” 和 第 10.2.3.2 节 “挂载事件触发的备份” 的例子。

### 9.4.16. Alt-SysRq 键

按 Alt-SysRq (PrtScr)组合键跟一个字母按键，进行不可思议的系统应急控制。

表 9.12. 著名的 SAK 命令键列表

Alt-SysRq 之后的键 | 行为描述
--- | ---
`k` | kill 杀死在当前虚拟控制台上的所有进程 (SAK)
`s` | sync 同步刷新所有已经挂载的文件系统来避免数据损坏
`u` | 重新以只读方式挂载所有已挂载的文件系统 (umount)
`r` | 在 X 崩溃后，从 raw 模式恢复键盘

更多信息参见 Linux 内核用户和管理员手册 » Linux Magic System Request Key Hacks

| 提示
从 SSH 终端等，你能够通过向 "`/proc/sysrq-trigger`" 写入内容来使用 Alt-SysRq 特性。例如，从 root shell 提示符运行 "`echo s > /proc/sysrq-trigger; echo u > /proc/sysrq-trigger`" 来 syncs 和 umounts 所有已挂载的文件系统。

目前(2021) Debian amd64 Linux 内核为 `/proc/sys/kernel/sysrq=438=0b110110110`:

- 2 = 0x2 - 启用控制台日志级别控制 (打开)

- 4 = 0x4 - 启用键盘控制 (SAK, unraw) (打开)

- 8 = 0x8 - 启用进程调试转储（debugging dumps of processes）等。（关闭）

- 16 = 0x10 - 启用 sync 命令(打开)

- 32 = 0x20 - 启用只读重新挂载（打开）

- 64 = 0x40 - 启用进程信号 (term, kill, oom-kill) (关闭)

- 128 = 0x80 - 允许重启、关闭电源（打开）

- 256 = 0x100 - 允许调整所有 RT（实时） 任务优先级（打开）

## 9.5. 系统维护技巧

### 9.5.1. 谁在系统里？

你可以通过下面的方法检查谁登录在系统里。

- who(1) 显示谁登录在系统里面。

- w(1) 显示谁登录在系统里面，他们正在在做什么。

- last(1) 显示用户最后登录的列表。

- lastb(1) 显示用户最后错误登录的列表。

| 提示
"`/var/run/utmp`" 和 "`/var/log/wtmp`" 存储这样的用户信息。参见 login(1) 和 utmp(5).

### 9.5.2. 警告所有人

你可以通过下面的方式使用 wall(1) 给登录系统的每一个人发送信息。

    $ echo "We are shutting down in 1 hour" | wall

### 9.5.3. 硬件识别

对于 PCI 类设备(AGP, PCI-Express, CardBus, ExpressCard 等), 一开始就使用 lspci(8) (也许加上 "`-nn`" 选项) 进行硬件识别比较好。

此外，你可以通过阅读 "`/proc/bus/pci/devices`" 里面的内容或浏览"`/sys/bus/pci`" 下面的目录树来进行硬件识别(参见 第 1.2.12 节 “procfs 和 sysfs”).

表 9.13. 硬件识别工具列表

软件包 | 流行度 | 大小 | 说明
--- | --- | --- | ---
`pciutils` | V:258, I:993 | 289 | Linux PCI 工具: lspci(8)
`usbutils` | V:84, I:888 | 322 | Linux USB 工具: lsusb(8)
`nvme-cli` | V:26, I:36 | 2222 | Linux NVMe 工具: nvme(1)
`pcmciautils` | V:4.0, I:6.0 | 92 | Linux PCMCIA 工具: pccardctl(8)
`scsitools` | V:0.2, I:2.2 | 261 | SCSI 硬件管理工具集: lsscsi(8)
`procinfo` | V:0.8, I:6.1 | 149 | 从 "`/proc`": lsdev(8) 获得系统信息
`lshw` | V:12, I:91 | 971 | 硬件配置信息: lshw(1)
`discover` | V:25, I:595 | 81 | 硬件识别系统: discover(8)

### 9.5.4. 硬件配置

像 GNOME 和 KDE 这类现代图形桌面系统，虽然大部分硬件的配置都能够通过相应的图形配置工具来管理，但知道一些配置它们的基础方式，也是一个好的主意。

For the keyboard and terminal configuration, see 第 8.2 节 “键盘输入” and 第 8.3 节 “显示输出”.

表 9.14. 硬件配置工具列表

软件包 | 流行度 | 大小 | 说明
--- | --- | --- | ---
`console-setup` | V:82, I:975 | 422 | Linux 控制台字体和键盘表工具
`keyd` | V:1.4, I:1.5 | 1437 | keyboard key remapping daemon using kernel level input primitives (evdev, uinput)
`keyd-application-mapper` | V:0.00, I:0.07 | 34 | keyboard key remapping daemon - application-specific remapper
`x11-xserver-utils` | V:304, I:528 | 559 | X 服务端工具: xset(1), xmodmap(1)
`acpid` | V:55, I:83 | 158 | 管理高级可配置和电源接口(ACPI)事件分发的后台守护进程（daemon）
`acpi` | V:7, I:77 | 49 | 显示 ACPI 设备信息的工具
`sleepd` | V:0.10, I:0.12 | 84 | 在笔记本空闲时，使其进入休眠状态的后台守护进程（daemon）
`hdparm` | V:114, I:208 | 246 | 硬盘访问优化 (参见 第 9.6.9 节 “硬盘优化”)
`smartmontools` | V:239, I:269 | 2455 | 使用 S.M.A.R.T. 控制和监控存储系统
`setserial` | V:3.2, I:5.2 | 104 | 串口管理工具集
`memtest86+` | V:1, I:19 | 12483 | 内存硬件管理工具集
`scsitools` | V:0.2, I:2.2 | 261 | SCSI 硬件管理工具集
`setcd` | V:0.06, I:0.67 | 33 | 光驱访问优化
`big-cursor` | I:0.65 | 26 | X 系统的大鼠标光标

这里, ACPI 是一个比 APM 新的电源管理系统框架。

| 提示
现代系统的 CPU 频率调整功能，是由内核模块 `acpi_cpufreq` 管理的。

### 9.5.5. 系统时间和硬件时间

下面设置系统的硬件时间为：MM/DD hh:mm, CCYY.

    # date MMDDhhmmCCYY
    # hwclock --utc --systohc
    # hwclock --show

Debian 系统的时间通常显示为本地时间，但硬件时间通常使用 UTC(GMT) 时间。

如果硬件时间设置为 UTC 时间，请在“`/etc/default/rcS`”里面设置“`UTC=yes`”。

下面是重新配置 Debian 系统使用的时区。

    # dpkg-reconfigure tzdata

如果你希望通过网络来更新系统时间，考虑使用 `ntp`, `ntpdate` 和 `chrony` 这类包提供的 NTP 服务。

| 提示
在 systemd 下，是使用 `systemd-timesyncd` 来替代进行网络时间同步。参见 systemd-timesyncd(8).

参见下面内容。

- 精确时间和日期管理 HOWTO

- NTP 公共服务项目

- `ntp-doc` 包

| 提示
在 `ntp` 包里面的 ntptrace(8) 能够跟踪 NTP 服务链至原始源。

### 9.5.6. 声音基础设施

用于现在的 Linux 的声卡设备驱动程序由 高级 Linux 声音体系（Advanced Linux Sound Architecture，ALSA） 提供。ALSA 提供了兼容之前的 开放声音系统（Open Sound System，OSS）的模拟模式。

应用软件不仅可被配置为不仅直接访问声音设备，也可以通过一些标准化声音服务端系统来访问它们。目前，PulseAudio、JACK 和 PipeWire 被用作声音的服务端系统。参见 Debian 维基声音页面 得到最新情况。

每个流行的桌面环境通常都有一个通用的声音引擎。每个被应用程序使用的声音引擎都可以选择连接到不同的声音服务器。

| 提示
使用 “`cat /dev/urandom > /dev/audio`” 或 speaker-test(1) 来测试扬声器（^C 停止）。

| 提示
如果你无法听到声音，那你的扬声器可能连接到了一个静音输出。现代的声音系统有许多输出。`alsa-utils` 软件包中的 alsamixer(1) 可以很好地配置声音和静音设置。

表 9.15. 声音软件包

软件包 | 流行度 | 大小 | 说明
--- | --- | --- | ---
`alsa-utils` | V:337, I:455 | 2700 | 配置和使用 ALSA 的工具
`oss-compat` | V:0.5, I:9.7 | 21 | 在 ALSA 下兼容 OSS，预防 “`/dev/dsp not found`” 错误
`pipewire` | V:321, I:364 | 135 | 音频和视频处理引擎多媒体服务端 - 元数据包
`pipewire-bin` | V:328, I:364 | 2219 | 音频和视频处理引擎多媒体服务端 - 音频服务和命令行程序
`pipewire-alsa` | V:168, I:234 | 190 | 音频和视频处理引擎多媒体服务端 - 代替 ALSA 的音频服务
`pipewire-pulse` | V:290, I:338 | 53 | 音频和视频处理引擎多媒体服务端 - 代替 PulseAudio 的音频服务
`pulseaudio` | V:151, I:177 | 6602 | PulseAudio 服务端
`libpulse0` | V:437, I:575 | 969 | PulseAudio 客户端库
`jackd` | V:2, I:14 | 8 | JACK Audio Connection Kit. (JACK) 服务器（低延迟）
`libjack0` | V:1.8, I:9.0 | 328 | JACK Audio Connection Kit. (JACK) 库（低延迟）
`libphonon4qt5-4` | V:20, I:46 | 572 | Phonon：KDE 声音引擎

### 9.5.7. 关闭屏幕保护

关闭屏幕保护，使用下面的命令。

表 9.16. 关闭屏幕保护命令列表

环境 | 命令
--- | ---
Linux 控制台 | `setterm -powersave off`
X 窗口（关闭屏幕保护） | `xset s off`
X 窗口（关闭 dpms） | `xset -dpms`
X 窗口（屏幕保护 GUI 配置） | `xscreensaver-command -prefs`

### 9.5.8. 关闭蜂鸣声

可以把电脑的扬声器拔掉来关闭蜂鸣声。把 `pcspkr` 内核模块删除，也可以做到这点。

bash(1) 用到的 readline(3) 程序，当遇到告警字符(ASCII=7)时，将会发生。下面的操作将阻止发生。

    $ echo "set bell-style none">> ~/.inputrc

### 9.5.9. 内存使用

对你来说，这里有两种可用的方法来得到内存的使用情况。

- “`/var/log/dmesg`” 中的内核启动信息包含了可用内存的精确总大小。

- free(1) 和 top(1) 显示正在运行的系统中内存资源的相关信息。

下面是一个例子。

    # grep '\] Memory' /var/log/dmesg
    [    0.004000] Memory: 990528k/1016784k available (1975k kernel code, 25868k reserved, 931k data, 296k init)
    $ free -k
                 total       used       free     shared    buffers     cached
    Mem:        997184     976928      20256          0     129592     171932
    -/+ buffers/cache:     675404     321780
    Swap:      4545576          4    4545572

你可能会觉得奇怪：“dmesg 告诉你 free 为 990 MB，而 free -k 告诉你 free 为 320 MB。这丢失了超过 600 MB ……”。

别担心 “`Mem:`” 这行中 “`used`” 较大的值以及 “`free`” 较小的值，放轻松，你需要查看的是下面的那个（在上面的例子中它们是 675404 和 321780）。

对于我的 MacBook，有 1GB=1048576k 内存（显卡系统用掉一些），我看到的如下。

表 9.17. 报告的内存大小

报告 | 大小
--- | ---
dmesg 中 total 的大小 | 1016784k = 1GB - 31792k
dmesg 中的 free | 990528k
shell 下的 total | 997184k
shell 下的 free | 20256k（但有效的为 321780k）

### 9.5.10. 系统安全性和完整性检查

糟糕的系统维护可能会暴露你的系统，导致它被外部非法使用。

对于系统安全性和完整性的检查，你需要从下面这些方面开始。

- `debsums` 软件包，参见 debsums(1) 和 第 2.5.2 节 “顶层“Release”文件及真实性”。

- `chkrootkit` 软件包，参见 chkrootkit(1)。

- `clamav` 软件包家族，参见 clamscan(1) 和 freshclam(1)。

- Debian security FAQ。

- Securing Debian Manual。

表 9.18. 用于系统安全性和完整性检查的工具

软件包 | 流行度 | 大小 | 说明
--- | --- | --- | ---
`logcheck` | V:4.7, I:5.8 | 120 | 后台守护进程（daemon），将系统日志文件中的异常通过邮件发送给管理员
`debsums` | V:4, I:30 | 108 | 实用程序，使用 MD5 校验码对已安装软件包的文件进行校验
`chkrootkit` | V:11, I:15 | 988 | rootkit 检测软件
`clamav` | V:9, I:40 | 19861 | Unix 的反病毒实用程序 —— 命令行界面
`tiger` | V:1.2, I:1.5 | 7800 | 报告系统安全漏洞
`tripwire` | V:1.3, I:1.7 | 5060 | 文件和目录完整性检测软件
`john` | V:1.1, I:7.7 | 469 | 先进的密码破解工具
`aide` | V:2.2, I:2.6 | 324 | 高级入侵环境检测 —— 静态二进制
`integrit` | V:0.09, I:0.18 | 2759 | 文件完整性验证程序
`crack` | V:0.09, I:0.65 | 153 | 密码猜测程序

下面是一个简单的脚本，用来检测典型的所有人可写的错误文件权限。

    # find / -perm 777 -a \! -type s -a \! -type l -a \! \( -type d -a -perm 1777 \)

| 小心
由于 `debsums` 软件包使用本地存储的 MD5 校验码，因此面对恶意攻击，也不能完全相信系统安全性检测工具。

## 9.6. 数据存储技巧

用 Linux 即用系统 (参见 第 3.2.2 节 “USB 上的 Linux 即用救援系统” 和 第 3.2.3 节 “从 GRUB 启动的 Linux 即用应急救援系统”) 启动你的系统，这将使在已经安装的系统上重新配置数据存储变得容易。

| 注意
对硬盘(HDD)的描述也适合用于其它存储设备，比如 SSD / U 盘 / 内存卡 /……。替换例子中 `/dev/sda` 的设备名称，用其适合的设备名称 `/dev/nvme0`、`/dev/mmcblk0`……。

如果设备在 GUI（图形用户界面）桌面系统下被自动挂载，在对它们进行操作前，你可能需要从命令行手工 umount(8) 这些设备。

### 9.6.1. 硬盘空间使用情况

硬盘空间的使用情况可以通过 `mount`、`coreutils` 和 `xdu` 软件包提供的程序来评估：

- mount(8) 显示所有挂载的文件系统(= 磁盘).

- df(1) 报告文件系统使用的硬盘空间。

- du(1) 报告目录树使用的硬盘空间。

| 提示
你可以将 du(8) 的输出传输给 xdu(1)，来使用它的图形交互式演示，例如 “`du -k . |xdu`”、“`sudo du -k -x / |xdu`” 等等。

### 9.6.2. 硬盘分区配置

对于硬盘分区配置，尽管 fdisk(8) 被认为是标准的配置，但是 parted(8) 工具还是值得注意的。

老的 PC 使用经典的主引导记录（Master Boot Record，MBR）方案，将硬盘分区数据保存在第一个扇区，即 LBA 扇区 0（512 字节）。

一些带有统一可扩展固件接口（Unified Extensible Firmware Interface，UEFI）的近代 PC，包括基于 Intel 的 Mac，使用 全局唯一标识分区表（GUID Partition Table，GPT）方案，硬盘分区数据不保存在第一个扇区。

尽管 fdisk(8) 一直是硬盘分区的标准工具，但现在 parted(8) 替代了它。

表 9.19. 硬盘分区管理软件包

软件包 | 流行度 | 大小 | 说明
--- | --- | --- | ---
`util-linux` | V:914, I:1000 | 4758 | 多种系统工具，包含 fdisk(8) 和 cfdisk(8)
`parted` | V:435, I:565 | 122 | GNU Parted，硬盘分区调整程序
`gparted` | V:11, I:87 | 2313 | 基于 `libparted` 的 GNOME 分区编辑程序
`gdisk` | V:19, I:264 | 967 | 用于 GPT/MBR 并存的硬盘的分区编辑程序
`kpartx` | V:16, I:26 | 80 | 为分区建立设备映射的程序

| 小心
尽管 parted(8) 声称也能用来创建和调整文件系统，但使用维护最好的专门工具来做这些事会更为安全，例如 mkfs(8)（mkfs.msdos(8)、mkfs.ext2(8)、mkfs.ext3(8)、mkfs.ext4(8)……）和 resize2fs(8)。

| 注意
为了在 GPT 和 MBR 之间切换，你需要直接删除开头的几个块中的内容（参见 第 9.8.6 节 “清空文件内容”）并使用 “`parted /dev/sdx mklabel gpt`” 或 “`parted /dev/sdx mklabel msdos`” 来设置它。请注意，这里使用的 “`msdos`” 是用于 MBR。

### 9.6.3. 使用 UUID 访问分区

尽管重新配置你的分区或可移动存储介质的激活顺序可能会给分区产生不同的名字，但你可以使用同一个 UUID 来访问它们。如果你有多个硬盘并且你的 BIOS/UEFI 没有给它们一致的设备名的话，使用 UUID 是不错的选择。

- mount(8) 命令带有 “`-U`” 选项可以使用UUID 来挂载一个块设备，而不必使用他的文件名称，例如 “`/dev/sda3`”。

- “`/etc/fstab`”（参见 fstab(5)）可以使用 UUID。

- 引载加载程序（第 3.1.2 节 “第二阶段：引载加载程序”）也可以使用 UUID。

| 提示
你可以使用 blkid(8) 来查看一个特定块设备的 UUID。 你也可以使用 "`lsblk -f`"来检测 UUID 并查看其它信息。

### 9.6.4. LVM2

LVM2 是一个用于 Linux 内核的逻辑卷管理器。使用 LVM2 的话，硬盘分区可以创建在逻辑卷上来替代物理硬盘。

LVM 有下列需求。

- Linux 内核中的设备映射支持（Debian 内核默认支持）

- 用户自定义设备映射支持库（`libdevmapper*` 软件包）

- 用户自定义 LVM2 工具（`lvm2` 软件包）

请从下面的 man 手册开始了解 LVM2。

- lvm(8)：LVM2 机制的基础知识（列出了所有 LVM2 命令）

- lvm.conf(5)：LVM2 的配置文件

- lvs(8)：报告逻辑卷的相关信息

- vgs(8)：报告卷组的相关信息

- pvs(8)：报告物理卷的相关信息

### 9.6.5. 文件系统配置

对于 ext4 文件系统, `e2fsprogs` 包提供下面的工具。

- mkfs.ext4(8) 创建新的 ext4 文件系统

- fsck.ext4(8) 检查和修复现有 ext4 文件系统

- tune2fs(8) 配置 ext4 文件系统的超级块

- debugfs(8) 交互式的调试 ext4 文件系统. (它有 `undel` 命令来恢复已经删除的文件.)

mkfs(8) 和 fsck(8) 命令是由 `e2fsprogs` 包提供的，是各种文件系统相关程序的前端。(`mkfs.fstype` 和 `fsck.fstype`). 对于 ext4 文件系统,它们是 mkfs.ext4(8) 和 fsck.ext4(8) (它们被符号链接到 mke2fs(8) 和 e2fsck(8)).

Linux 支持的每一个文件系统，有相似的命令。

表 9.20. 文件系统管理包列表

软件包 | 流行度 | 大小 | 说明
--- | --- | --- | ---
`e2fsprogs` | V:828, I:998 | 1560 | ext2/ext3/ext4 文件系统工具
`btrfs-progs` | V:52, I:79 | 5255 | Btrfs 文件系统工具
`reiserfsprogs` | V:10, I:21 | 473 | Reiserfs 文件系统工具
`zfsutils-linux` | V:33, I:33 | 1904 | OpenZFS 文件系统工具
`dosfstools` | V:260, I:564 | 310 | FAT 文件系统工具. (Microsoft: MS-DOS, Windows)
`exfatprogs` | V:35, I:459 | 558 | exFAT 文件系统工具，由三星维护。
`exfat-fuse` | V:2, I:43 | 73 | FUSE 读写 exFAT 文件系统（微软）驱动。
`xfsprogs` | V:41, I:86 | 4382 | XFS 文件系统工具. (SGI: IRIX)
`ntfs-3g` | V:175, I:509 | 1507 | FUSE 读写 NTFS 文件系统（微软：Windows NT……）驱动。
`jfsutils` | V:0.4, I:6.7 | 1104 | JFS 文件系统工具. (IBM: AIX, OS/2)
`xorriso` | V:14, I:65 | 378 | ISO-9660 文件系统工具 和 来自 libburnia 的 CD/DVD 刻录工具
`wodim` | V:7, I:87 | 898 | 来自 cdrkit 的命令行 CD/DVD 刻录工具
`genisoimage` | V:16, I:158 | 1567 | 来自 cdrkit 的 ISO-9660 文件系统命令行工具
`reiser4progs` | V:0.1, I:1.2 | 1367 | Reiser4 文件系统工具
`hfsprogs` | V:0.3, I:3.2 | 394 | HFS 和 HFS Plus 文件系统工具. (Apple: Mac OS)
`zerofree` | V:7, I:117 | 30 | 把 ext2/3/4 文件系统上空闲块设置为零的程序

| 提示
Ext4 文件系统是 Linux 系统上默认的文件系统，强烈推荐使用这个文件系统，除非你有特殊的理由不使用。 Btrfs 状态能够在 Debian wiki on btrfs 和 kernel.org wiki on btrfs 发现。它被期望作为 ext4 文件系统之后的下一个默认文件系统。 一些工具可以在没有 Linux 内核支持的情况下访问文件系统(参见 第 9.8.2 节 “不挂载磁盘操作文件”).

### 9.6.6. 文件系统创建和完整性检查

mkfs(8) 在 Linux 系统上创建文件系统。fsck(8) 命令在 Linux系统上提供文件系统完整性检查和修复功能。

在文件系统创建后，Debian 现在默认不周期性的运行 `fsck`。

| 小心
在已经挂载的文件系统上运行 `fsck` ，一般是不安全的.

| 提示
在 "`/etc/mke2fs.conf`" 里设置 "`enable_periodic_fsck`" 并使用 "`tune2fs -c0 /dev/partition_name`" 设置最大挂载数为 0，便可以在重启时，让 root 文件系统包括在内的所有文件系统上，安全的运行fsck(8) 命令. 参见 mke2fs.conf(5) 和 tune2fs(8). 从启动脚本里面运行的 fsck(8) 命令结果，可以在 "`/var/log/fsck/`" 目录下查看。

### 9.6.7. 通过挂载选项优化文件系统

“`/etc/fstab`” 中包含了基础的静态文件系统配置。例如，

    «file system»                   «mount point» «type» «options»    «dump» «pass»
    proc                                      /proc proc   defaults          0 0
    UUID=709cbe4c-80c1-56db-8ab1-dbce3146d2f7 /     ext4   errors=remount-ro 0 1
    UUID=817bae6b-45d2-5aca-4d2a-1267ab46ac23 none  swap   sw                0 0
    /dev/scd0                        /media/cdrom0  udf,iso9660 user,noauto  0 0

| 提示
UUID（参见 第 9.6.3 节 “使用 UUID 访问分区”）可以替代一般的块设备名称（例如 “`/dev/sda1`”、“`/dev/sda2`”……）来识别一个块设备。

从 Linux 2.6.30 起，内核的默认行为是提供"`relatime`"选项。

参见 fstab(5) 和 mount(8)。

### 9.6.8. 通过超级块（superblock）优化文件系统

一个文件系统的特性可以使用 tune2fs(8) 命令通过超级块来优化。

- 执行 “`sudo tune2fs -l /dev/sda1`” 可以显示 “`/dev/sda1`” 上的文件系统超级块内容。

- 执行"`sudo tune2fs -c 50 /dev/sda1`" 改变 "`/dev/sda1`" 文件系统检查(在启动时执行 `fsck`)的频率为每 50 次启动.

- 执行 “`sudo tune2fs -j /dev/sda1`” 会给文件系统添加日志功能，即 “`/dev/sda1`” 的文件系统从 ext2 转换为 ext3。（对未挂载的文件系统这么做。）

- 执行 “`sudo tune2fs -O extents,uninit_bg,dir_index /dev/sda1 && fsck -pf /dev/sda1`” 在 “`/dev/sda1`” 上将它从 ext3 转换为 ext4。（对未挂载的系统这么做。）

| 提示
尽管 tune2fs(8) 的名字是这样的，但它不仅能用于 ext2 文件系统，也能用于 ext3 和 ext4 文件系统。

### 9.6.9. 硬盘优化

| 警告
在你折腾硬盘配置之前，请检查你的硬件并阅读 hdparm(8) 的 man 手册页，因为这可能会对数据完整性造成相当大的危害。

你可以通过 “`hdparm -tT /dev/sda`” 来测试 “`/dev/sda`” 硬盘的访问速度。对于一些使用 (E)IDE 连接的硬盘，你可以使用 “`hdparm -q -c3 -d1 -u1 -m16 /dev/sda`” 来启用 “(E)IDE 32 位支持”、启用 “using_dma flag”、设置 “interrupt-unmask flag” 并设置 “multiple 16 sector I/O”（危险！），从而加速硬盘访问速度。

你可以通过 “`hdparm -W /dev/sda`” 来测试 “`/dev/sda`” 硬盘的写入缓存功能。你可以使用 “`hdparm -W 0 /dev/sda`” 关闭写入缓存功能。

现代高速 CD-ROM 光驱，你可以使用 "`setcd -x 2`" 降低速度，来读取不当压缩的 CDROM 光盘。

### 9.6.10. 固态硬盘优化

固态硬盘（Solid state drive，SSD） 目前可以被自动检测。

在 `/etc/fstab` 里面，将易失性数据路径挂载为 "`tmpfs`"，可以减少不必要的磁盘访问来阻止磁盘损耗。

### 9.6.11. 使用 SMART 预测硬盘故障

你可以使用兼容 SMART 的 smartd(8) 后台守护进程（daemon）来监控和记录你的硬盘。

1. 在 BIOS 中启用 SMART 功能。

2. 安装 `smartmontools` 软件包。

3. 通过 df(1) 列出硬盘驱动并识别它们。

   - 让我们假设要监控的硬盘为 “`/dev/sda`”。

4. 检查 “`smartctl -a /dev/sda`” 的输出，看 SMART 功能是否已启用。

   - 如果没有，通过 “`smartctl -s on -a /dev/sda`” 启用它。

5. 通过下列方式运行 smartd(8) 后台守护进程（daemon）。

   - 消除 `/etc/default/smartmontools`” 文件中 “`start_smartd=yes`” 的注释。

   - 通过 “`sudo systemctl restart smartmontools`” 重新启动 smartd(8) 后台守护进程（daemon）。

| 提示
smartd(8) 后台守护进程（daemon）可以使用 `/etc/smartd.conf` 文件进行自定义，文件中包含了相关的警告。

### 9.6.12. 通过 $TMPDIR 指定临时存储目录

应用程序一般在临时存储目录 “`/tmp`” 下建立临时文件。如果 “`/tmp`” 没有足够的空间，你可以通过 `$TMPDIR` 变量来为程序指定临时存储目录。

### 9.6.13. 通过 LVM 扩展可用存储空间

在安装时创建在 Logical Volume Manager 逻辑卷管理(LVM) (Linux 特性) 上的分区，它们可以容易的通过合并扩展或删除扩展的方式改变大小，而不需要在多个存储设备上进行大量的重新配置。

### 9.6.14. 通过挂载另一个分区来扩展可用存储空间

如果你有一个空的分区（例如 “`/dev/sdx`”），你可以使用 mkfs.ext4(1) 将它格式化，并使用 mount(8) 将它挂载到你需要更多空间的目录。（你需要复制原始数据内容。）

    $ sudo mv work-dir old-dir
    $ sudo mkfs.ext4 /dev/sdx
    $ sudo mount -t ext4 /dev/sdx work-dir
    $ sudo cp -a old-dir/* work-dir
    $ sudo rm -rf old-dir

| 提示
你也可以选择挂载一个空硬盘映像文件（参见 第 9.7.5 节 “制作空的磁盘映像文件”）作为一个循环设备（参见 第 9.7.3 节 “挂载磁盘映像文件”）。实际的硬盘使用量会随着实际存储数据的增加而增加。

### 9.6.15. 通过 “mount --bind” 挂载另一个目录来扩展可用存储空间

如果你在另一个分区里有一个带有可用空间的空目录（例如 “`/path/to/emp-dir`”），你可以通过带有 “`--bind`” 选项的 mount(8)，将它挂载到一个你需要更多空间的目录（例如 “`work-dir`”）。

    $ sudo mount --bind /path/to/emp-dir work-dir

### 9.6.16. 通过 overlay 挂载（overlay-mounting）另一个目录来扩展可用存储空间

如果你在另一个分区表中有可用的空间（例如，“`/path/to/empty`” 和 “`/path/to/work`”），你可以在其中建立一个目录并堆栈到你需要空间的那个旧的目录（例如，“`/path/to/old`”），要这样做，你需要用于 Linux 3.18 版内核或更新版本（对应 Debian Stetch 9.0 或更新版本）的 OverlayFS。

    $ sudo mount -t overlay overlay \
      -olowerdir=/path/to/old-dir,upperdir=/path/to/empty,workdir=/path/to/work

“`/path/to/empty`” 和 “`/path/to/work`” 应该位于可读写的分区，从而能够写入 “`/path/to/old`”。

### 9.6.17. 使用符号链接扩展可用存储空间

| 小心
这是一个已弃用的做法。某些软件在遇到“软链接目录”时可能不会正常工作。请优先使用上文所述的“挂载”的途径。

如果你在另一个分区里有一个带有可用空间的空目录（例如 “`/path/to/emp-dir`”），你可以使用 ln(8) 建立目录的一个符号链接。

    $ sudo mv work-dir old-dir
    $ sudo mkdir -p /path/to/emp-dir
    $ sudo ln -sf /path/to/emp-dir work-dir
    $ sudo cp -a old-dir/* work-dir
    $ sudo rm -rf old-dir

| 警告
别对由系统管理的目录（例如 “`/opt`”）使用“链接到目录”，这样的链接在系统升级时可能会被覆盖。

## 9.7. 磁盘映像

我们在这里讨论磁盘影响的操作。

### 9.7.1. 制作磁盘映像文件

一个未挂载设备（例如，第二个 SCSI 或 串行 ATA 设备 “`/dev/sdb`”）的磁盘映像文件 “`disk.img`” ，可以使用下列方式之一建立。

    # dd if=/dev/sdb of=disk.img; sync

    # cp /dev/sdb disk.img ; sync

    # cat /dev/sdb > disk.img ; sync

传统 PC 中位于主 IDE 硬盘第一扇区的主引导记录（MBR）（参见 第 9.6.2 节 “硬盘分区配置”）的磁盘映像可以使用 dd(1) 通过下列方式建立。

    # dd if=/dev/sda of=mbr.img bs=512 count=1
    # dd if=/dev/sda of=mbr-nopart.img bs=446 count=1
    # dd if=/dev/sda of=mbr-part.img skip=446 bs=1 count=66

- “`mbr.img`”：带有分区表的 MBR

- “`mbr-nopart.img`”：不带分区表的 MBR

- “`mbr-part.img`”：仅 MBR 的分区表

如果你要建立原始硬盘的一个硬盘分区的映像，你需要使用 “`/dev/sda1`” 等替代 “`/dev/sda`”。

### 9.7.2. 直接写入硬盘

磁盘映像文件 “`disk.img`” 可以通过下列方式之一写入到一个匹配大小的未挂载设备（例如，第二个 SCSI 设备 `“/dev/sdb`”。

    # dd if=disk.img of=/dev/sdb ; sync

    # cp disk.img /dev/sdb ; sync

    # cat disk.img >/dev/sdb ; sync

相似地，硬盘分区映像文件 “`partition.img`” 可以通过下列方式写入到匹配大小的未挂载分区（例如，第二个 SCSI 设备的第一个分区 “`/dev/sdb1`”）。

    # dd if=partition.img of=/dev/sdb1 ; sync

### 9.7.3. 挂载磁盘映像文件

可以使用循环设备通过下列方式挂载和卸载包含单个分区映像的磁盘映像 “`partition.img`”。

    # losetup --show -f partition.img
    /dev/loop0
    # mkdir -p /mnt/loop0
    # mount -t auto /dev/loop0 /mnt/loop0
    ...hack...hack...hack
    # umount /dev/loop0
    # losetup -d /dev/loop0

可以简化为如下步骤。

    # mkdir -p /mnt/loop0
    # mount -t auto -o loop partition.img /mnt/loop0
    ...hack...hack...hack
    # umount partition.img

可以使用 循环设备 挂载包含多个分区的磁盘映像 “`disk.img`” 的每个分区。

    # losetup --show -f -P disk.img
    /dev/loop0
    # ls -l /dev/loop0*
    brw-rw---- 1 root disk   7,  0 Apr  2 22:51 /dev/loop0
    brw-rw---- 1 root disk 259, 12 Apr  2 22:51 /dev/loop0p1
    brw-rw---- 1 root disk 259, 13 Apr  2 22:51 /dev/loop0p14
    brw-rw---- 1 root disk 259, 14 Apr  2 22:51 /dev/loop0p15
    # fdisk -l /dev/loop0
    Disk /dev/loop0: 2 GiB, 2147483648 bytes, 4194304 sectors
    Units: sectors of 1 * 512 = 512 bytes
    Sector size (logical/physical): 512 bytes / 512 bytes
    I/O size (minimum/optimal): 512 bytes / 512 bytes
    Disklabel type: gpt
    Disk identifier: 6A1D9E28-C48C-2144-91F7-968B3CBC9BD1

    Device         Start     End Sectors  Size Type
    /dev/loop0p1  262144 4192255 3930112  1.9G Linux root (x86-64)
    /dev/loop0p14   2048    8191    6144    3M BIOS boot
    /dev/loop0p15   8192  262143  253952  124M EFI System

    Partition table entries are not in disk order.
    # mkdir -p /mnt/loop0p1
    # mkdir -p /mnt/loop0p15
    # mount -t auto /dev/loop0p1 /mnt/loop0p1
    # mount -t auto /dev/loop0p15 /mnt/loop0p15
    # mount |grep loop
    /dev/loop0p1 on /mnt/loop0p1 type ext4 (rw,relatime)
    /dev/loop0p15 on /mnt/loop0p15 type vfat (rw,relatime,fmask=0002,dmask=0002,allow_utime=0020,codepage=437,iocharset=ascii,shortname=mixed,utf8,errors=remount-ro)
    ...hack...hack...hack
    # umount /dev/loop0p1
    # umount /dev/loop0p15
    # losetup -d /dev/loop0

或者，你也可以使用 `kpartx` 软件包中的 kpartx(8) 建立 设备映射设备来达到相同的效果。

    # kpartx -a -v disk.img
    add map loop0p1 (253:0): 0 3930112 linear 7:0 262144
    add map loop0p14 (253:1): 0 6144 linear 7:0 2048
    add map loop0p15 (253:2): 0 253952 linear 7:0 8192
    # fdisk -l /dev/loop0
    Disk /dev/loop0: 2 GiB, 2147483648 bytes, 4194304 sectors
    Units: sectors of 1 * 512 = 512 bytes
    Sector size (logical/physical): 512 bytes / 512 bytes
    I/O size (minimum/optimal): 512 bytes / 512 bytes
    Disklabel type: gpt
    Disk identifier: 6A1D9E28-C48C-2144-91F7-968B3CBC9BD1

    Device         Start     End Sectors  Size Type
    /dev/loop0p1  262144 4192255 3930112  1.9G Linux root (x86-64)
    /dev/loop0p14   2048    8191    6144    3M BIOS boot
    /dev/loop0p15   8192  262143  253952  124M EFI System

    Partition table entries are not in disk order.
    # ls -l /dev/mapper/
    total 0
    crw------- 1 root root 10, 236 Apr  2 22:45 control
    lrwxrwxrwx 1 root root       7 Apr  2 23:19 loop0p1 -> ../dm-0
    lrwxrwxrwx 1 root root       7 Apr  2 23:19 loop0p14 -> ../dm-1
    lrwxrwxrwx 1 root root       7 Apr  2 23:19 loop0p15 -> ../dm-2
    # mkdir -p /mnt/loop0p1
    # mkdir -p /mnt/loop0p15
    # mount -t auto /dev/mapper/loop0p1 /mnt/loop0p1
    # mount -t auto /dev/mapper/loop0p15 /mnt/loop0p15
    # mount |grep loop
    /dev/loop0p1 on /mnt/loop0p1 type ext4 (rw,relatime)
    /dev/loop0p15 on /mnt/loop0p15 type vfat (rw,relatime,fmask=0002,dmask=0002,allow_utime=0020,codepage=437,iocharset=ascii,shortname=mixed,utf8,errors=remount-ro)
    ...hack...hack...hack
    # umount /dev/mapper/loop0p1
    # umount /dev/mapper/loop0p15
    # kpartx -d disk.img

### 9.7.4. 清理磁盘映像文件

使用下面的方式，一个磁盘映像文件 "`disk.img`" 能够清理掉所有已经删除的文件，成为一个干净的稀疏映像 "`new.img`"。

    # mkdir old; mkdir new
    # mount -t auto -o loop disk.img old
    # dd bs=1 count=0 if=/dev/zero of=new.img seek=5G
    # mount -t auto -o loop new.img new
    # cd old
    # cp -a --sparse=always ./ ../new/
    # cd ..
    # umount new.img
    # umount disk.img

如果 “`disk.img`” 位于 ext2、ext3 或 ext4，你也可以像下面那样使用 `zerofree` 软件包中的 zerofree(8)。

    # losetup --show -f disk.img
    /dev/loop0
    # zerofree /dev/loop0
    # cp --sparse=always disk.img new.img
    # losetup -d /dev/loop0

### 9.7.5. 制作空的磁盘映像文件

按下面的方式使用 dd(1) ，可以制作一个大小为 5GB 的空磁盘映像文件。

    $ dd bs=1 count=0 if=/dev/zero of=disk.img seek=5G

专业的 fallocate(8) 可以在这里被使用，用来替代使用 dd(1)。

按下面的方式使用环回设备，你能够在这个磁盘映像"`disk.img`"上创建 ext4 文件系统。

    # losetup --show -f disk.img
    /dev/loop0
    # mkfs.ext4 /dev/loop0
    ...hack...hack...hack
    # losetup -d /dev/loop0
    $ du  --apparent-size -h disk.img
    5.0G  disk.img
    $ du -h disk.img
    83M disk.img

对于 "`disk.img`",它的文件大小是5.0 GiB，而它实际磁盘使用仅仅是 83MiB.这个差距可能是由于 ext4 里面有稀疏文件.

| 提示
稀疏文件的实际磁盘使用会随着数据的写入而增加。

回环设备 或 设备映射 设备上使用类似的操作，在这些设备按 第 9.7.3 节 “挂载磁盘映像文件” 挂载后, 你能够使用 parted(8) 或 fdisk(8)对这个磁盘映像"`disk.img`"进行分区，能够使用 mkfs.ext4(8), mkswap(8)在上面创建文件系统等.

### 9.7.6. 制作 ISO9660 镜像文件

| 提示
cdrkit 提供的 genisoimage(1) 和 Libburnia 提供的 xorrisofs(1) ，这两个命令共享相同的命令语法，仅命令名称不一样。

"`源目录`"下的目录树可以通过如下所示的 cdrkit 提供的 genisoimage(1) 命令来制作 ISO9660 镜像文件，"`cd.iso`"。

    #  genisoimage -r -J -T -V volume_id -o cd.iso source_directory

类似的，可引导的 ISO9660 镜像文件，"`cdboot.iso`", 能够从 `debian-installer` 类似目录树 "`source_directory`" 制作，方式如下。

    #  genisoimage -r -o cdboot.iso -V volume_id \
       -b isolinux/isolinux.bin -c isolinux/boot.cat \
       -no-emul-boot -boot-load-size 4 -boot-info-table source_directory

这里的 Isolinux boot loader (参见 第 3.1.2 节 “第二阶段：引载加载程序”) 是用于启动的.

按下面的方式，你可以直接从光驱设备计算 md5sum 值，并制作 ISO9660 镜像。

    $ isoinfo -d -i /dev/cdrom
    CD-ROM is in ISO 9660 format
    ...
    Logical block size is: 2048
    Volume size is: 23150592
    ...
    # dd if=/dev/cdrom bs=2048 count=23150592 conv=notrunc,noerror | md5sum
    # dd if=/dev/cdrom bs=2048 count=23150592 conv=notrunc,noerror > cd.iso

| 警告
为了得到正确结果，你必须小心避免 Linux ISO9600 文件系统预读 bug。

### 9.7.7. 直接写入文件到 CD/DVD-R/RW

| 提示
对 cdrkit提供的wodim(1) 和 Libburnia 提供的 xorrecord(1)而言，DVD 只是一个大的 CD。这些命令共享相同的命令语法，仅命令名称不一样。

你能够通过如下所示的命令找到可用的设备。

    # wodim --devices

然后将空的 CD-R 插入 CD 驱动器并且把 ISO9660 镜像文件，"`cd.iso`" 写入到设备中，例如用如下所示的 wodim(1) 将数据写入到 "`/dev/sda`" 设备。

    # wodim -v -eject dev=/dev/sda cd.iso

如果用 CD-RW 代替 CD-R，用如下所示的命令来替代。

    # wodim -v -eject blank=fast dev=/dev/sda cd.iso

| 提示
如果你的桌面系统自动挂载 CDs，在使用 wodim(1) 之前在终端里面用 "`sudo umount /dev/sda`" 卸载它。

### 9.7.8. 挂载 ISO9660 镜像文件

如果 "`cd.iso`" 包含一个 ISO9660 镜像, 下面的命令手工挂载这个文件到 "`/cdrom`".

    # mount -t iso9660 -o ro,loop cd.iso /cdrom

| 提示
现代桌面系统能够自动挂载可移动介质，如按 ISO9660 格式化的 CD(参见 第 10.1.7 节 “可移动存储设备”).

## 9.8. 二进制数据

这里，我们讨论直接操作存储介质上的二进制数据。

### 9.8.1. 查看和编辑二进制数据

最基础的查看二进制数据的方法是使用 "`od -t x1`" 命令。

表 9.21. 查看和修改二进制数据的软件包列表

软件包 | 流行度 | 大小 | 说明
--- | --- | --- | ---
`coreutils` | V:910, I:1000 | 17994 | 基础软件包，有 od(1) 来导出文件(HEX, ASCII, OCTAL, …)
`bsdmainutils` | V:4, I:131 | 17 | 工具软件包，有 hd(1) 来导出文件(HEX, ASCII, OCTAL, …)
`hexedit` | V:0.8, I:8.1 | 70 | 二进制浏览和编辑器(HEX, ASCII)
`bless` | V:0.1, I:1.4 | 924 | 全功能的十六进制编辑器(GNOME)
`okteta` | V:1, I:10 | 1589 | 全功能的十六进制编辑器(KDE4)
`ncurses-hexedit` | V:0.1, I:1.3 | 130 | 二进制浏览和编辑器(HEX, ASCII, EBCDIC)
`beav` | V:0.04, I:0.30 | 137 | 二进制浏览和编辑器(HEX, ASCII, EBCDIC, OCTAL, ...)

| 提示
HEX 是十六进制英文hexadecimal首字母缩略词，基数 radix 是 16。OCTAL 是八进制英文octal 首字母缩略词，基数 radix是 8。ASCII是美国信息交换标准代码 American Standard Code for Information Interchange 的英文缩写，即正常的英语文本代码。EBCDIC是扩展二进制编码十进制交换码 Extended Binary Coded Decimal Interchange Code 的英文缩写，在 IBM 大型机 操作系统上使用。

### 9.8.2. 不挂载磁盘操作文件

有工具可以在没有挂载磁盘的情况下读写文件。

表 9.22. 不挂载磁盘操作文件的软件包列表

软件包 | 流行度 | 大小 | 说明
--- | --- | --- | ---
`mtools` | V:7, I:53 | 400 | 不挂载磁盘的 MSDOS 文件工具
`hfsutils` | V:0.2, I:2.8 | 178 | 不挂载磁盘的 HFS 和 HFS+ 文件工具

### 9.8.3. 数据冗余

Linux 内核所提供的RAID软件系统提供内核文件系统级别的数据冗余来实现高水平的存储可靠性。

有在应用程序级别增加数据冗余来实现高水平存储可靠性的工具。

表 9.23. 向文件添加数据冗余的工具列表

软件包 | 流行度 | 大小 | 说明
--- | --- | --- | ---
`par2` | V:9, I:116 | 297 | 奇偶校验档案卷设置，用于检查和修复文件
`dvdisaster` | V:0.1, I:1.2 | 1422 | CD/DVD 媒体数据损失/划伤/老化的保护
`dvbackup` | V:0.01, I:0.04 | 413 | 使用 MiniDV 便携式摄像机的备份工具(提供 rsbep(1))

### 9.8.4. 数据文件恢复和诊断分析

有用于数据文件恢复和诊断分析的工具。

表 9.24. 数据文件恢复和诊断分析软件包列表

软件包 | 流行度 | 大小 | 说明
--- | --- | --- | ---
`testdisk` | V:2, I:26 | 2276 | 分区扫描和磁盘恢复的实用程序
`magicrescue` | V:0.2, I:1.9 | 257 | 通过查找幻数 magic 字节来恢复文件的工具（译注：请 man file 来了解幻数）
`scalpel` | V:0.2, I:2.4 | 89 | 简洁、高性能的文件提取
`myrescue` | V:0.2, I:2.1 | 81 | 恢复损坏硬盘中的数据
`extundelete` | V:0.5, I:7.8 | 152 | 恢复删除 ext3/4 文件系统上的文件的实用程序
`ext4magic` | V:0.3, I:3.7 | 235 | 恢复删除 ext3/4 文件系统上的文件的实用程序
`ext3grep` | V:0.2, I:2.0 | 299 | 帮助恢复 ext3 文件系统上删除的文件的工具
`scrounge-ntfs` | V:0.2, I:1.7 | 49 | NTFS 文件系统的数据恢复程序
`gzrt` | V:0.02, I:0.47 | 34 | gzip 恢复工具包
`sleuthkit` | V:2, I:25 | 1119 | 诊断分析工具(Sleuthkit)
`autopsy` | V:0.2, I:1.3 | 1026 | SleuthKit 的图形化界面
`foremost` | V:0.4, I:4.2 | 101 | 恢复数据的诊断程序
`guymager` | V:0.14, I:0.82 | 1049 | 基于 Qt 的诊断图像工具
`dcfldd` | V:0.3, I:2.8 | 113 | 增强版的 `dd`，用于诊断和安全

| 提示
在 `e2fsprogs` 软件包里有 debugfs(8) 命令，使用该命令里的 `list_deleted_inodes` 和 `undel` 指令，你能够恢复 ext2 文件系统上删除的文件。

### 9.8.5. 把大文件分成多个小文件

当一个文件太大而不能备份的时候，你应该在备份之前先把它分割为多个小于 2000MiB 的小文件，稍后再把这些小文件合并为初始的文件。

    $ split -b 2000m large_file
    $ cat x* >large_file

| 小心
为了防止文件名冲突，请确保没有任何以 "`x`" 开头的文件。

### 9.8.6. 清空文件内容

为了清除诸如日志文件之类的文件的内容，不要用 rm(1) 命令去删除文件然后创建新的空文件，因为这个文件可能在命令执行的期间还在被使用。以下是清除文件内容的正确方法。

    $ :>file_to_be_cleared

### 9.8.7. 样子文件

下面的命令创建样子文件或空文件。

    $ dd if=/dev/zero    of=5kb.file bs=1k count=5
    $ dd if=/dev/urandom of=7mb.file bs=1M count=7
    $ touch zero.file
    $ : > alwayszero.file

你将发现下列文件。

- "`5kb.file`" 是 5KB 的全零数据。

- "`7mb.file`" 是 7MB 随机数据。

- "`zero.file`" 也许是一个 0 字节的文件。如果这个文件之前就存在，则它的 `mtime` 会被更新，而它的内容和长度保持不变。

- "`alwayszero.file`" 一定是一个 0 字节文件。如果这个文件之前存在，则它的 `mtime` 会被更新，而它的内容会被清零。

### 9.8.8. 擦除整块硬盘

有几种方法来完全擦除设备上整个硬盘上数据，比如说，在 "`/dev/sda`" 上的 USB 盘。

| 小心
在执行这里的命令之前，你应该用 mount(8) 命令来查看 USB 盘 的挂载位置。"`/dev/sda`" 指向的设备可能是装有整个系统的 SCSI 硬盘或者 serial-ATA 硬盘。

如下所示是通过数据归 0 的方式来擦除硬盘上所有数据的。

    # dd if=/dev/zero of=/dev/sda

如下是用随机数据重写的方式来擦除所有数据的。

    # dd if=/dev/urandom of=/dev/sda

如下是用随机数据重写的方式来高效擦除所有数据。

    # shred -v -n 1 /dev/sda

你或者可以使用 badblocks(8) 加上 `-t random` 选项。

因为 dd(1) 命令在许多可引导的 Linux CDs (例如 Debian 安装光盘) 上的 shell 环境下都是可用的，你能够在装有系统的硬盘上，例如 "`/dev/sda`", "`/dev/sda`" 等等设备上运行擦除命令来完全清除已经安装的系统。

### 9.8.9. 擦除硬盘上的未使用的区域

硬盘（或 USB 盘）上未使用的区域，例如“`/dev/sdb1`”可能仍然包含可被擦除的数据，因为他们本身只是解除了从文件系统的链接，这些可以通过重写来清除。

    # mount -t auto /dev/sdb1 /mnt/foo
    # cd /mnt/foo
    # dd if=/dev/zero of=junk
    dd: writing to `junk': No space left on device
    ...
    # sync
    # umount /dev/sdb1

| 警告
这对您的 USB 盘 来说通常已经足够好了，但这还不完美。大部分已擦除的文件名和它们的属性可能隐藏并留在文件系统中。

### 9.8.10. 恢复已经删除但仍然被打开的文件

即使你不小心删除了某个文件，只要这个文件仍然被一些应用程序所使用（读或者写），恢复此文件是可能的。

尝试下列例子

    $ echo foo > bar
    $ less bar
    $ ps aux | grep ' less[ ]'
    bozo    4775  0.0  0.0  92200   884 pts/8    S+   00:18   0:00 less bar
    $ rm bar
    $ ls -l /proc/4775/fd | grep bar
    lr-x------ 1 bozo bozo 64 2008-05-09 00:19 4 -> /home/bozo/bar (deleted)
    $ cat /proc/4775/fd/4 >bar
    $ ls -l
    -rw-r--r-- 1 bozo bozo 4 2008-05-09 00:25 bar
    $ cat bar
    foo

当你安装了 `lsof` 软件包的时候，在另外一个终端执行如下命令。

    $ ls -li bar
    2228329 -rw-r--r-- 1 bozo bozo 4 2008-05-11 11:02 bar
    $ lsof |grep bar|grep less
    less 4775 bozo 4r REG 8,3 4 2228329 /home/bozo/bar
    $ rm bar
    $ lsof |grep bar|grep less
    less 4775 bozo 4r REG 8,3 4 2228329 /home/bozo/bar (deleted)
    $ cat /proc/4775/fd/4 >bar
    $ ls -li bar
    2228302 -rw-r--r-- 1 bozo bozo 4 2008-05-11 11:05 bar
    $ cat bar
    foo

### 9.8.11. 查找所有硬链接

有硬链接的文件，能够使用 "`ls -li`" 确认。

    $ ls -li
    total 0
    2738405 -rw-r--r-- 1 root root 0 2008-09-15 20:21 bar
    2738404 -rw-r--r-- 2 root root 0 2008-09-15 20:21 baz
    2738404 -rw-r--r-- 2 root root 0 2008-09-15 20:21 foo

"`baz`" 和 "`foo`" 的链接数为 "2" (>1)，表示他们有硬链接。它们的 inode 号都是"2738404".这表示它们是同样的硬链接文件。如果你不想偶然碰巧发现硬链接文件，你可以通过 inode 号来查找它。比如说, 按下面的方式查找 "2738404" 。

    # find /path/to/mount/point -xdev -inum 2738404

### 9.8.12. 不可见磁盘空间消耗

所有打开的文件被删除后，仍然消耗磁盘空间，尽管他们不能够被普通的 du(1) 所看见。这些被删除的文件和他们的大小，可以通过下面的方式列出。

    # lsof -s -X / |grep deleted

## 9.9. 数据加密提示

在可以物理访问您的 PC的情况下，任何人都可以轻易获得 root 权限，访问您的 PC 上的所有文件 (见 第 4.6.4 节 “root 密码安全”)。 这意味着登录密码系统在您的 PC 被偷盗时并不能保证您私人和敏感数据的安全。您必须部署数据加密技术来实现。尽管 GNU 隐私守护 (见 第 10.3 节 “数据安全基础”) 可以对文件进行加密，但它需要一些用户端的工作。

Dm-crypt 通过原生的 Linux 内核模块，使用 device-mapper，用很少的用户操作实现本地自动数据加密。

表 9.25. 数据加密工具列表

软件包 | 流行度 | 大小 | 说明
--- | --- | --- | ---
`cryptsetup` | V:35, I:80 | 465 | 可用于加密的块设备 的实用程序（dm-crypt / 3LUKS）
`cryptmount` | V:2.0, I:2.6 | 236 | 可用于加密的块设备着重于正常用户挂载/卸载的实用程序（ dm-crypt / LUKS）
`fscrypt` | V:0.4, I:1.1 | 6545 | Linux 文件系统加密工具(fscrypt)
`libpam-fscrypt` | V:0.29, I:0.34 | 6594 | Linux 文件系统加密 PAM 模块(fscrypt)

| 小心
数据加密会消耗 CPU 时间等资源，如果它的密码丢失，加密的数据会变成无法访问。请权衡其利弊。

| 注意
通过 debian-installer (lenny 或更新版)，整个 Debian 系统能够被安装到一个加密的磁盘上，使用 dm-crypt/LUKS 和 initramfs.

| 提示
请参阅 第 10.3 节 “数据安全基础” 用户空间加密实用程序： GNU Privacy Guard。

### 9.9.1. 使用 dm-crypt/LUKS 加密移动磁盘

您可以用 dm-crypt/LUKS 加密大容量可移动设备上的数据，例如挂载在“`/dev/sdx`”上的 USB 盘。你只需按如下步骤简单地把它格式化。

    # fdisk /dev/sdx
    ... "n" "p" "1" "return" "return" "w"
    # cryptsetup luksFormat /dev/sdx1
    ...
    # cryptsetup open /dev/sdx1 secret
    ...
    # ls -l /dev/mapper/
    total 0
    crw-rw---- 1 root root  10, 60 2021-10-04 18:44 control
    lrwxrwxrwx 1 root root       7 2021-10-04 23:55 secret -> ../dm-0
    # mkfs.vfat /dev/mapper/secret
    ...
    # cryptsetup close secret

然后，它就可以正常的在现代桌面环境下使用 `udisks2` 软件包，挂载到 "`/media/username/disk_label`"，只不过它会要求输入密码 (参见 第 10.1.7 节 “可移动存储设备”)。不同的是写入的数据都是加密的。密码条目可以自动使用钥匙环（参见 第 10.3.6 节 “密码密钥环”）。

你可以把它格式化成其他格式的文件系统，例如用 "`mkfs.ext4 /dev/mapper/sdx1`" 把它格式化为 ext4。如果使用 btrfs，则需要安装 `udisks2-btrfs` 软件包。对于这些文件系统，可能需要配置文件的所有者和权限。

### 9.9.2. 使用 dm-crypt/LUKS 挂载加密的磁盘

举个列子，用 dm-crypt/LUKS 在"`/dev/sdc5`"上创建的加密磁盘可以用如下步骤挂载到"`/mnt`"：

    $ sudo cryptsetup open /dev/sdc5 ninja --type luks
    Enter passphrase for /dev/sdc5: ****
    $ sudo lvm
    lvm> lvscan
      inactive          '/dev/ninja-vg/root' [13.52 GiB] inherit
      inactive          '/dev/ninja-vg/swap_1' [640.00 MiB] inherit
      ACTIVE            '/dev/goofy/root' [180.00 GiB] inherit
      ACTIVE            '/dev/goofy/swap' [9.70 GiB] inherit
    lvm> lvchange -a y /dev/ninja-vg/root
    lvm> exit
      Exiting.
    $ sudo mount /dev/ninja-vg/root /mnt

## 9.10. 内核

对于支持的架构，Debian 使用软件包来分发模块化的 Linux内核.

如果你正在阅读本文档，你可能不需要亲自编译内核。

### 9.10.1. 内核参数

许多 Linux 特性可以按下面的方式，通过内核参数来配置。

- 内核参数通过 bootloader 初始化(参见 第 3.1.2 节 “第二阶段：引载加载程序”)

- 对通过 sysfs 访问的内核参数，在运行时通过 sysctl(8) 修改(参见 第 1.2.12 节 “procfs 和 sysfs”)

- 当一个模块被激活时，通过 modprobe(8) 参数来设置模块参数。(参见 第 9.7.3 节 “挂载磁盘映像文件”)

细节参见 "The Linux kernel user’s and administrator’s guide » The kernel’s command-line parameters" 。

### 9.10.2. 内核头文件

大部分普通程序编译时不需要内核头文件，如果你直接使用它们来编译，甚至会导致编译中断。在 Debian 系统上，普通程序编译依赖 `libc6-dev` 软件包 (由 `glibc` 源代码包创建)提供的，在"`/usr/include/linux`" 和 "`/usr/include/asm`" 里的头文件。

| 注意
对于编译一些内核相关的程序，比如说从外部源代码编译的内核模块和 automounter 后台守护（daemon）程序(`amd`),你必须包含相应的内核头文件到路径里，比如"`-I/usr/src/linux-particular-version/include/`", 到你的命令行。

### 9.10.3. 编译内核和相关模块

Debian 有它自己的方式来编译内核和相关模块。

表 9.26. Debian 系统内核编译需要安装的主要软件包列表

软件包 | 流行度 | 大小 | 说明
--- | --- | --- | ---
`build-essential` | V:18, I:512 | 17 | 创建 Debian 软件包所必须的软件包: `make`, `gcc`, …
`bzip2` | V:159, I:972 | 113 | bz2 文件压缩和解压缩工具
`libncurses5-dev` | I:34 | 6 | ncurses 开发者库和文档
`git` | V:409, I:623 | 52512 | git：Linux 内核使用的分布式版本控制系统
`fakeroot` | V:32, I:514 | 225 | 为非 root 用户创建软件包提供一个伪造的 root 环境
`initramfs-tools` | V:479, I:988 | 49 | 创建 initramfs 的工具（Debian 规范）
`dkms` | V:81, I:149 | 235 | 动态内核模块支持 dynamic kernel module support (DKMS) (通用)
`module-assistant` | V:1, I:13 | 395 | 制作模块软件包的帮助工具（Debian 规范）
`devscripts` | V:5, I:33 | 2766 | Debian 软件包维护者的帮助脚本（Debian 规范）

如果你在 第 3.1.2 节 “第二阶段：引载加载程序” 使用 `initrd` , 请一定阅读 initramfs-tools(8), update-initramfs(8), mkinitramfs(8) 和initramfs.conf(5) 里的相关信息。

| 警告
在编译 Linux 内核源代码时，请不要放置从"`/usr/include/linux`" 和 "`/usr/include/asm`" 到源代码树(比如："`/usr/src/linux*`") 里目录的符号链接。(一些过期的文档建议这样做.)

| 注意
当在 Debian `stable` 版里编译最新的 Linux 内核时, 可能需要使用一些从 Debian `unstable` 版里 向后移植（backported）的工具的最新版本。 module-assistant(8) (或者它的短形式 `m-a`) 帮助用户，从一个或多个定制的内核，容易的构建和安装模块软件包。 dynamic kernel module support (DKMS)动态内核模块支持 是一个新的分布式独立框架，被设计用来允许单个的内核模块在不改变整个内核的情况下升级。这可以用于维护内核代码树外部的模块。这也使你升级内核时，重新编译模块变得非常简单。

### 9.10.4. 编译内核源代码：Debian 内核团队推荐

从上游内核源代码编译个性化的内核二进制包，你应当使用由它提供的 "`deb-pkg`" 对象。

    $ sudo apt-get build-dep linux
    $ cd /usr/src
    $ wget https://mirrors.edge.kernel.org/pub/linux/kernel/v7.x/linux-7.1.7.tar.xz
    $ tar --xz -xvf linux-7.1.7.tar.xz
    $ cd linux-7.1.7
    $ cp /boot/config-7.1.7.config
    $ make menuconfig
     ...
    $ make deb-pkg

| 提示
linux-source-version 软件包使用 "`/usr/src/linux-version.tar.bz2`" 提供有 Debian 补丁的 Linux内核源代码。

从 Debian 内核源代码软件包编译特定的二进制包，你应当使用 "`debian/rules.gen`" 里的 "`binary-arch_architecture_featureset_flavour`" 对象。

    $ sudo apt-get build-dep linux
    $ apt-get source linux
    $ cd linux-7.*
    $ fakeroot make -f debian/rules.gen binary-arch_i386_none_686

进阶信息参见：

- Debian Wiki: KernelFAQ

- Debian Wiki: DebianKernel

- Debian Linux 内核手册：https://kernel-handbook.debian.net

### 9.10.5. 硬件驱动和固件

硬件驱动是运行在目标系统上主 CPU 上的代码。大部分硬件驱动现在是自由软件，已经包含在普通的 Debian 内核软件包里，放在 `main` 区域。

- GPU 驱动

  - Intel GPU 驱动 (`main`)

  - AMD/ATI GPU 驱动 (`main`)和/

  - NVIDIA GPU 驱动 (nouveau 驱动放在 `main` , 由厂家支持的二进制驱动，放在 `non-free`.)

固件是加载在连接在目标系统设备上的代码或数据(比如说，CPU microcode、 GPU 运行的渲染代码 或 FPGA / CPLD 数据……）部分固件包是作为自由软件存在，但是很多固件包由于包含有没有源代码的数据，二进制不是作为自由软件存在。安装这些固件数据是必需的，这样设备才能按期望运行。

- 固件数据软件包，含有加载到目标设备易失性存储器上的数据。

  - firmware-linux-free (`main`)

  - firmware-linux-nonfree (`non-free-firmware`)

  - firmware-linux-* (`non-free-firmware`)

  - *-firmware (`non-free-firmware`)

  - intel-microcode (`non-free-firmware`)

  - amd64-microcode (`non-free-firmware`)

- 固件更新程序软件包，更新在目标设备非易失性存储器上的数据。

  - fwupd (`main`)：从 Linux Vendor Firmware Service 下载固件数据的固件更新后台守护进程（daemon）。

  - gnome-firmware (`main`): fwupd 的 GTK 前端

  - plasma-discover-backend-fwupd (`main`): fwupd 的 Qt 前端

请注意，从 Debian 12 Bookworm 开始，用户使用由官方安装介质里面提供的 `non-free-firmware` 软件包来提供完善的安装体验。 `non-free-firmware` 区域在 第 2.1.5 节 “Debian 档案库基础” 里面描述。

也请注意到， fwupd 从 Linux Vendor Firmware Service 下载的固件数据并加载到正在运行的 Linux 内核，可能是 `non-free`。

## 9.11. 虚拟化系统

通过使用虚拟系统，我们能在单个机器上同时运行多个系统。

| 提示
参见 Debian wiki 的系统虚拟化。

### 9.11.1. 虚拟化和模拟器工具

有几个 虚拟化 和模拟器工具平台。

- 完全的 硬件模拟 软件包，比如通过 games-emulator 元软件包安装的软件包

- 大部分 CPU 层的模拟，加上一些 I/O 设备的模拟，比如 QEMU

- 大部分 CPU 层的虚拟化，加上一些 I/O 设备的模拟，比如 Kernel-based Virtual Machine (KVM)

- 操作系统级的容器虚拟化，加上内核级的支持，比如 LXC (Linux Containers), Docker, systemd-nspawn(1), ...

- 操作系统级的文件系统访问虚拟化，加上在文件路径上的系统库调用，比如 chroot

- 操作系统级的文件系统访问虚拟化，加上在文件所有者权限上的系统库调用，比如 fakeroot

- 操作系统 API 模拟器，比如 Wine

- 解释器级的虚拟化，加上它的执行选择和运行时库忽略，比如 Python 的 virtualenv 和 venv

容器虚拟化使用 第 4.7.5 节 “Linux 安全特性”，是 第 7.7 节 “沙盒” 的后端技术。

这里有一些帮你搭建虚拟化系统的软件包。

表 9.27. 虚拟化工具列表

软件包 | 流行度 | 大小 | 说明
--- | --- | --- | ---
`coreutils` | V:910, I:1000 | 17994 | GNU 核心工具包含 chroot(8)
`util-linux` | V:914, I:1000 | 4758 | 其他系统工具包含 unshare(8)
`systemd-container` | V:74, I:77 | 2726 | systemd container/nspawn 工具包含 systemd-nspawn(1)
`schroot` | V:7.6, I:9.5 | 2222 | 在 chroot 下执行 Debian 二进制包的特异工具
`sbuild` | V:3.8, I:7.2 | 155 | 从 Debian 源码构建 Debian 二进制包的工具
`debootstrap` | V:4, I:46 | 331 | 搭建一个基本的 Debian 系统 (用 sh 写的)
`mmdebstrap` | V:8, I:13 | 574 | 搭建一个 Debian 系统 (用 Perl 写的)
`cdebootstrap` | V:0.1, I:1.3 | 114 | 搭建一个 Debian 系统 (用 C 写的)
`cloud-image-utils` | V:1, I:14 | 52 | 云镜像管理工具
`cloud-guest-utils` | V:5, I:23 | 67 | 云客户机工具
`virt-manager` | V:11, I:48 | 2309 | 虚拟机管理器: 用于管理虚拟机的桌面应用
`libvirt-clients` | V:48, I:72 | 1164 | libvirt 的库程序
`docker.io` | V:50, I:52 | 81699 | docker: Linux 容器运行时
`podman` | V:33, I:36 | 84895 | podman：在 Pods 里面运行基于 OCI 容器的引擎
`podman-docker` | V:2.7, I:3.3 | 270 | 在 Pods 里面运行基于 OCI 容器的引擎 -- docker 封装器
`incus` | V:0.8, I:3.2 | 23 | Incus：系统容器和虚拟机管理器
`games-emulator` | I:0.19 | 21 | games-emulator: Debian 的游戏模拟器
`bochs` | V:0.05, I:0.62 | 8180 | Bochs: IA-32 PC 仿真器
`qemu-system` | I:21 | 64 | QEMU: 全功能系统的模拟二进制
`qemu-user` | V:5.8, I:9.6 | 471927 | QEMU: 用户模式的模拟二进制
`qemu-utils` | V:13, I:113 | 12634 | QEMU: 工具集
`qemu-system-x86` | V:53, I:96 | 69241 | KVM: x86 硬件上有 硬件辅助虚拟化的全虚拟化
`virtualbox` | V:2.7, I:3.4 | 151006 | VirtualBox:i386 和 amd64 上 x86 的虚拟化解决方案
`gnome-boxes` | V:1.1, I:6.5 | 7074 | Boxes：用于访问虚拟机系统的简单的 GNOME 应用程序
`xen-tools` | V:0.1, I:1.5 | 719 | 用于管理 debian XEN 虚拟服务器的工具
`wine` | V:13, I:56 | 204 | Wine: Windows 应用程序编程接口实现（标准套件）
`dosbox` | V:1, I:12 | 2697 | DOSBox:有 Tandy/Herc/CGA/EGA/VGA/SVGA 显卡,声音和 DOS 的 x86 模拟器
`lxc` | V:10, I:13 | 1681 | Linux 容器用户层工具
`python3-venv` | V:8, I:153 | 6 | venv 创建虚拟的 python 环境（系统库）
`python3-virtualenv` | V:7, I:39 | 374 | virtualenv 创建隔离的虚拟 python 环境
`pipx` | V:8, I:50 | 4412 | pipx 在隔离的环境中安装 python 应用程序

参见维基百科 Comparison of platform virtual machines 来获得不同平台的虚拟化解决方案的详细比较信息。

### 9.11.2. 虚拟化工作流

| 注意
自从 `lenny` 之后，默认的 Debian 内核就是支持 KVM 的。

典型的虚拟化工作流涉及以下几个步骤。

- 创建空文件系统 (目录树或磁盘映像)。

  - 目录树可以通过 "`mkdir -p /path/to/chroot`" 创建。

  - 原始的磁盘映像文件能够使用 dd(1) 创建 (参见 第 9.7.1 节 “制作磁盘映像文件” 和 第 9.7.5 节 “制作空的磁盘映像文件”).

  - qemu-img(1) 能够创建和转化 QEMU 支持的磁盘映像文件.

  - 原始的格式和 VMDK 文件格式,能够作为虚拟化工具的通用格式.

- 使用 mount(8) 挂载磁盘映像到文件系统 (可选).

  - 对于原始磁盘映像文件,把它作为回环设备 或 设备映射 设备挂载. (参见 第 9.7.3 节 “挂载磁盘映像文件”).

  - 对于 QEMU 支持的磁盘映像,把它们作为 network block device 网络块设备挂载 (参见 第 9.11.3 节 “挂载虚拟磁盘映像文件”).

- 在目标文件系统上部署需要的系统数据。

  - 使用 `debootstrap` 和 `cdebootstrap` 之类的程序来协助处理这个过程(参见 第 9.11.4 节 “Chroot 系统”).

  - 在全功能系统模拟器下使用操作系统安装器。

- 在虚拟化环境下运行一个程序。

  - chroot 提供基本的虚拟化环境,足够能在里面编译程序,运行控制台应用,运行后台守护程序 daemon.

  - QEMU 提供跨平台的 CPU 模拟器.

  - QEMU 和 KVM 通过 hardware-assisted virtualization硬件辅助虚拟化来提供全功能系统的模拟.

  - VirtualBox 可以在 i386 和 amd64 上,使用或者不使用 hardware-assisted virtualization硬件辅助虚拟化来提供全功能系统模拟.

### 9.11.3. 挂载虚拟磁盘映像文件

对于原始磁盘映像文件,参见 第 9.7 节 “磁盘映像”.

对于其它虚拟磁盘映像文件,你能够用使用 network block device网络块设备 协议的 qemu-nbd(8) 来导出他们,并使用内核模块 `nbd` 来挂载它们.

qemu-nbd(8) 支持 QEMU 所支持的磁盘格式: raw, qcow2, qcow, vmdk, vdi, bochs, cow (user-mode Linux copy-on-write), parallels, dmg, cloop, vpc, vvfat (virtual VFAT)和主机设备.

网络块设备 能够用和回环设备一样的方式支持分区 (参见 第 9.7.3 节 “挂载磁盘映像文件”). 你能够按下面的方式挂载 "`disk.img`" 的第一个分区.

    # modprobe nbd max_part=16
    # qemu-nbd -v -c /dev/nbd0 disk.img
    ...
    # mkdir /mnt/part1
    # mount /dev/nbd0p1 /mnt/part1

| 提示
你可以给 qemu-nbd(8) 使用 "`-P 1`" 选项来导出"`disk.img`"的第一个分区.

### 9.11.4. Chroot 系统

如果你希望从终端控制台尝试一个新的 Debian 环境，我推荐你使用 chroot。这使你能够运行 `unstable` 和 `testing` 的控制台应用程序，不会有通常的相关风险，并且不需要重启。chroot(8) 是最基础的方法。

| 小心
下面的列子假设根源系统和 chroot 系统都共享相同的 `amd64` CPU 架构。

虽然你能够手工使用 debootstrap(1)来创建一个 chroot(8) 环境，这要求琐碎的工作。

sbuild 软件包从源代码构建一个 Debian 软件包，使用 schroot 软件包管理的 chroot 环境。它和帮助脚本 sbuild-createchroot(1) 一起。让我们按如下所示的方式运行它，来学会它是怎样工作的。

    $ sudo mkdir -p /srv/chroot
    $ sudo sbuild-createchroot -v --include=eatmydata,ccache unstable /srv/chroot/unstable-amd64-sbuild http://deb.debian.org/debian
     ...

你能够看到 debootstrap(8) 是如何在 "`/srv/chroot/unstable-amd64-sbuild`" 下部署 `unstable` 环境的系统数据,用于一个最小的构建系统。

你可以使用 schroot(1) 来登录到这个环境。

    $ sudo schroot -v -c chroot:unstable-amd64-sbuild

你可以看到一个运行在 `unstable` 环境的系统 shell 是如何创建的。

| 注意
"`/usr/sbin/policy-rc.d`" 文件总是用 101 退出，阻止在 Debian 系统上自动启动后台守护程序。参见 "`/usr/share/doc/init-system-helpers/README.policy-rc.d.gz`"。

| 注意
一些在 chroot 下的程序，需要访问比上面根源系统上的 `sbuild-createchroot` 能够提供的文件之外的更多文件才能够运行。例如，"`/sys`", "`/etc/passwd`", "`/etc/group`", "`/var/run/utmp`", "`/var/log/wtmp`"等等.也许需要使用 bind-mounted 或拷贝.

| 提示
`sbuild` 软件包帮助建立一个 chroot 系统来构建一个软件包，在 chroot 内使用 `schroot` 作为它的后端。它是一个检查构建依赖（build-dependencies）的理想系统。更多信息参见 sbuild at Debian wiki 和在"Guide for Debian Maintainers"中的 sbuild 配置样例 。

| 提示
systemd-nspawn(1) 命令使用 `chroot` 类似的方法帮助运行一个命令，或者轻量级容器内的操作系统。它更强大，因为它使用命名空间来完全虚拟化进程树、进程间通讯、主机名、域名，并可选网络和用户数据库。 参见 systemd-nspawn。

### 9.11.5. 多桌面系统

如果你希望尝试任一操作系统的一个新的 GUI 桌面环境，我推荐在 Debian `稳定版` 系统上使用 QEMU或者KVM ，这些软件应用虚拟化技术安全的运行多桌面系统。这能让你运行任何桌面应用，包括 Debian `不稳定版`和`测试版`上的桌面应用，并且没有与之相关的通常意义上的风险，并且不需要重启。

因为单纯的 QEMU 工具是非常慢的，当主机系统支持 KVM 的时候，建议使用它来加速。

虚拟机管理器，也被称为 `virt-manager`，它是一个便利的管理 KVM 虚拟机的 GUI（图形用户界面）工具，它调用 libvirt。

按下面的方法，能够创建一个可以用于QEMU 的包含有 Debian 系统的虚拟磁盘映像 "`virtdisk.qcow2`"，这个 Debian 系统使用 debian 安装器:小 CD安装。

    $ wget https://cdimage.debian.org/debian-cd/5.0.3/amd64/iso-cd/debian-503-amd64-netinst.iso
    $ qemu-img create -f qcow2 virtdisk.qcow2 5G
    $ qemu -hda virtdisk.qcow2 -cdrom debian-503-amd64-netinst.iso -boot d -m 256
    ...

| 提示
在虚拟化下运行 Ubuntu 和 Fedora 之类的其它 GNU/Linux 发行版，是一个不错的学习其配置技巧的方法。其它专有操作系统也可以在这个 GNU/Linux 虚拟化下很好的运行。

在 Debian wiki: SystemVirtualization 参看更多技巧。

[2] 更多精心制作的定制例子： "Vim Galore", "sensible.vim", ...

[3] vim-pathogen 也很流行。

第 8 章 国际化和本地化 |  | 第 10 章 数据管理