├── .gitattributes
├── .github
└── workflows
│ └── build.yml
├── .gitignore
├── .markdownlint.jsonc
├── .prettierignore
├── .prettierrc.json
├── LICENSE
├── Makefile
├── README.md
├── docs
├── Appendix
│ ├── distribution.md
│ ├── images
│ │ └── yast.png
│ ├── man.md
│ └── markdown.md
├── Ch01
│ ├── images
│ │ ├── Android-10-Native.png
│ │ ├── Android-Stack.png
│ │ ├── Android-TV.png
│ │ ├── Arch-Linux-Logo.png
│ │ ├── CentOS-Logo.png
│ │ ├── Copyleft.png
│ │ ├── Debian-Logo.png
│ │ ├── Fedora-Logo.png
│ │ ├── LUG@USTC-Logo.png
│ │ ├── Manjaro-Logo.png
│ │ ├── Performance-Era.jpg
│ │ ├── Red-Hat-Logo.png
│ │ ├── Ubuntu-Logo.png
│ │ ├── VWP-Xubuntu-32bit-Desktop.png
│ │ ├── VWP-Xubuntu-32bit-Login.png
│ │ ├── VirtualBox-Create-VM.jpg
│ │ ├── VirtualBox-import.jpg
│ │ ├── Windows-Server.png
│ │ ├── Xubuntu-Logo.png
│ │ └── applesilicon_vmware
│ │ │ ├── 1.png
│ │ │ ├── 2.png
│ │ │ ├── 3.png
│ │ │ ├── 4.png
│ │ │ ├── 6.png
│ │ │ └── 7.png
│ ├── index.md
│ ├── solution.md
│ └── supplement.md
├── Ch02
│ ├── images
│ │ ├── Plasma-desktop.png
│ │ ├── Plasma-themes-store.png
│ │ ├── Xfce-Autostart.png
│ │ ├── Xfce-FM-list.png
│ │ ├── Xfce-FM.png
│ │ ├── Xfce-WM-keyboard.png
│ │ ├── Xfce-WM-style-installation.png
│ │ ├── Xfce-WM-style.png
│ │ ├── Xfce-appearance-fonts.png
│ │ ├── Xfce-appearance-icons.png
│ │ ├── Xfce-menu-applocations.png
│ │ ├── Xfce-menu-workspaces.png
│ │ ├── Xfce-panel-display.png
│ │ ├── Xfce-panel-items.png
│ │ ├── Xfce-sessions.png
│ │ ├── Xfce-settings-FM.png
│ │ ├── Xfce-settings-appearance.png
│ │ ├── Xfce-settings-background.png
│ │ ├── Xfce-settings-desktop.png
│ │ ├── Xfce-settings-icons.png
│ │ ├── Xfce-settings-menu.png
│ │ ├── Xfce-settings-position.png
│ │ ├── Xfce-settings.png
│ │ ├── Xfce-style-installation.png
│ │ ├── Xfce-style-installation_2.png
│ │ ├── Xfce-terminal-pos.png
│ │ ├── Xfce-terminal.png
│ │ ├── Xfce-top-panel.png
│ │ ├── caffeine-folder.png
│ │ ├── caffeine.png
│ │ ├── conky-manager.png
│ │ ├── conky.png
│ │ ├── download-theme.png
│ │ ├── extensions-management.png
│ │ ├── gnome-desktop.png
│ │ ├── gnome-extensions.png
│ │ ├── gnome-tweak-tool.png
│ │ ├── jekyll-installation.png
│ │ ├── metadata.png
│ │ ├── ocs-url-download.png
│ │ ├── themes-download-icon.png
│ │ ├── wordpress-installation.png
│ │ ├── xfce-look-mainpage.png
│ │ └── xfce-look-theme.png
│ ├── index.md
│ ├── solution.md
│ ├── supplement.md
│ └── wordpress.sh
├── Ch03
│ ├── images
│ │ ├── link.png
│ │ └── vscode-ubuntu-application-store.png
│ ├── index.md
│ ├── solution.md
│ └── supplement.md
├── Ch04
│ ├── images
│ │ ├── PID.png
│ │ ├── abstract.png
│ │ ├── add_column.png
│ │ ├── bg.gif
│ │ ├── bg.png
│ │ ├── crontab.gif
│ │ ├── fg_bg.png
│ │ ├── forking.png
│ │ ├── htop.gif
│ │ ├── privilege.png
│ │ ├── services.png
│ │ ├── signal_slide.png
│ │ ├── state.png
│ │ ├── test.gif
│ │ ├── time_slice.png
│ │ ├── tmux.gif
│ │ ├── top8.png
│ │ └── type_kill.png
│ ├── index.md
│ ├── solution.md
│ └── supplement.md
├── Ch05
│ ├── assets
│ │ └── unix_filesystem.png
│ ├── index.md
│ ├── solution.md
│ └── supplement.md
├── Ch06
│ ├── images
│ │ └── pipe.png
│ ├── index.md
│ ├── solution.md
│ └── supplement.md
├── Ch07
│ ├── index.md
│ └── supplement.md
├── Ch08
│ ├── index.md
│ └── supplement.md
├── Ch09
│ ├── index.md
│ └── supplement.md
├── Ch10
│ ├── images
│ │ └── ms-store-search-linux.png
│ └── index.md
├── Spec
│ ├── slide.md
│ └── writing.md
├── credits.md
├── css
│ ├── extra.css
│ ├── extra.scss
│ └── roboto.css
├── images
│ └── heading-id-failure.png
├── index.md
├── notations.md
├── postface.md
└── preface.md
├── mkdocs.yml
├── package-lock.json
├── package.json
└── requirements.txt
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: build
2 |
3 | on:
4 | push:
5 | branches:
6 | - '*'
7 | pull_request:
8 | branches:
9 | - '*'
10 | workflow_dispatch: {}
11 |
12 | jobs:
13 | build:
14 | runs-on: ubuntu-latest
15 | steps:
16 | - uses: actions/checkout@v3
17 | - uses: actions/setup-python@v4
18 | with:
19 | python-version: '3.10'
20 | - uses: actions/setup-node@v3
21 | with:
22 | node-version: 16
23 | - name: Install dependencies
24 | run: |
25 | pip3 install -r requirements.txt
26 | npm install
27 | - name: Markdown style check (prettier)
28 | run: |
29 | npm run check
30 | - name: Build docs
31 | run: |
32 | mkdocs -v build
33 | : > site/.nojekyll
34 | echo -n '101.ustclug.org' > site/CNAME
35 | - name: Deploy to GitHub Pages
36 | if: github.ref == 'refs/heads/master'
37 | run: |
38 | CINFO="$(git log -1 --pretty="%an: [%h] %s")"
39 | git clone --depth=1 --branch=gh-pages --single-branch --no-checkout \
40 | "https://${GITHUB_ACTOR}:${{ secrets.GITHUB_TOKEN }}@github.com/${GITHUB_REPOSITORY}.git" test
41 | mv test/.git site/.git && rmdir test/
42 | pushd site/ &>/dev/null
43 | git add -A
44 | git -c user.name=GitHub -c user.email=noreply@github.com commit \
45 | -m "Auto deploy from GitHub Actions build ${GITHUB_RUN_NUMBER}" \
46 | -m "$CINFO"
47 | git push
48 | popd &>/dev/null
49 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .sass-cache
2 | .DS_Store
3 | env/
4 | venv/
5 | venv-mkdocs/
6 | site/
7 | .env/
8 |
9 | node_modules/
--------------------------------------------------------------------------------
/.markdownlint.jsonc:
--------------------------------------------------------------------------------
1 | {
2 | "MD007": {
3 | // Unordered list indentation
4 | "indent": 4
5 | },
6 | "MD013": false, // Line length
7 | "MD033": {
8 | // Inline HTML
9 | "allowed_elements": [
10 | "br", // Useful in tables
11 | "figure",
12 | "figcaption",
13 | "s",
14 | "del" // Python-Markdown parsing issue with CJK
15 | ]
16 | },
17 | "MD046": false, // Use fenced code block style, too many false positives
18 | "MD051": false, // Link fragments should be valid, false positives
19 | "MD052": false, // Reference link should be defined and used, false positives with includes/man.md
20 | "MD010": false, // Some command output contains hard tabs
21 | "MD024": {
22 | // Allow multiple headers with the same content, if they are not siblings
23 | "siblings_only": true
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | .git
2 | .github
3 | .sass-cache
4 | .DS_Store
5 | env/
6 | venv/
7 | venv-mkdocs/
8 | .env/
9 |
10 | site/
11 |
12 | mkdocs.yml
13 | docs/css/
14 |
15 | package*.json
--------------------------------------------------------------------------------
/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "embeddedLanguageFormatting": "off",
3 | "tabWidth": 4
4 | }
5 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .PHONY: all css
2 |
3 | all:
4 | mkdocs build
5 |
6 | css: docs/css/extra.css
7 |
8 | docs/css/extra.css: docs/css/extra.scss
9 | sassc -t compact $^ > $@
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # [Linux 101 Docs](https://101.lug.ustc.edu.cn/)
2 |
3 | ## 如何构建?
4 |
5 | 1. (建议)使用 `python3 -m venv venv` 创建虚拟环境,`. venv/bin/activate` 进入虚拟环境;
6 | 2. 使用 `pip install -r requirements.txt` 安装依赖;
7 | 3. 使用 `mkdocs serve` 构建并查看效果。
8 |
9 | ## 编写建议
10 |
11 | 请参考 [编写指导](https://101.lug.ustc.edu.cn/Spec/writing/) 页面,了解我们的编写规范。
12 |
13 | ## 许可
14 |
15 | 本文档以 Creative Commons BY-SA 4.0 协议发布。详情请见 [LICENSE](LICENSE)。
16 |
--------------------------------------------------------------------------------
/docs/Appendix/distribution.md:
--------------------------------------------------------------------------------
1 | # 其他的 Linux 发行版:技术差异简介
2 |
3 | 在 Linux 101 讲义中,我们默认使用的发行版是 Ubuntu。当然,可能你正在使用其他的 Linux 发行版。尽管大部分的内容都是通用的,但发行版之间仍然或多或少存在一些差别。下面会简要介绍其他的发行版中需要注意的地方。
4 |
5 | ## Debian GNU/Linux,与其他的 Debian 衍生版本 {#debian-and-derivation}
6 |
7 | Ubuntu 基于 Debian,并且相比 Debian 而言更加新手友好。而 Debian 的开发周期更慢,它的 Stable 分支也更加稳定。在很多方面来说,它们的区别不大,但是仍然需要注意一些事情:
8 |
9 | - 不同的发行版、不同的分支的软件源不能混用。向 Debian 添加 Ubuntu 或 Ubuntu PPAs 的源可能会导致软件依赖的混乱。
10 | - Debian 不会预置一些 Ubuntu 特有的特性。从 Snapcraft 商店、Livepatch(在不停机的情况下修复内核漏洞的服务)到 ZSys(由 Ubuntu 开发的 ZFS 管理工具)都不会预置在 Debian 中。
11 | - 在日常使用中,Debian 也有一些小的区别,例如默认情况下,`/sbin` 不在普通用户的 PATH 中。
12 |
13 | ## CentOS 与 Fedora {#centos-and-fedora}
14 |
15 | ### 软件包管理 {#rh-software-management}
16 |
17 | 「红帽系」的 Linux 发行版与 Ubuntu 等 Debian 系列的发行版最直观的区别就在于它们使用的软件包管理方式不一样。一般使用 `dnf`(推荐)或者 `yum` 来进行软件包管理。`dnf` 和 `yum` 都是使用 Python 编写的程序,谨慎变动系统 Python 环境,以免出现问题。
18 |
19 | 简单使用:
20 |
21 | ```console
22 | $ sudo dnf install audacity # 安装 audacity
23 | $ sudo dnf remove firefox # 卸载 firefox
24 | $ dnf search thunderbird # 搜索 thunderbird
25 | $ sudo dnf upgrade # 更新系统
26 | ```
27 |
28 | 此外,`dnf` 和 `yum` 还可以回滚到以前的软件安装状态,具体可以自行搜索了解。
29 |
30 | ### 关于 SELinux {#selinux}
31 |
32 | SELinux 是由 NSA 编写的开源的 Linux 安全模块,在 CentOS 和 Fedora 上都默认开启。SELinux 解决的问题是,传统的 DAC(自主访问控制, Discretionary Access Control)安全模型(我们在第五章中看到的 `rwx` 就是传统的模型)无法有效应对一些安全风险,如[^1]:
33 |
34 | - 用户可能会把「任何人都可读取」的权限赋予在敏感文件(如 SSH 密钥)上。
35 | - 用户的进程可以修改文件的安全性属性。例如,邮件程序可以(尽管不应该)将邮件文件设置为「任何人都可读取」。
36 | - 用户的进程继承用户的权限,如果进程本身有问题或是不安全,那么攻击者可以以该用户的权限作任何事情。例如,如果浏览器被攻击,它可以读取到用户的 SSH 密钥,但浏览器显然不应该做这种事情。
37 |
38 | SELinux 添加了额外的「强制访问控制」安全措施:系统中所有的文件、进程和端口等都被贴上了 SELinux 标签,如果访问者(Subject)和被访问对象(Object)的标签不符合规则,访问则会被拒绝。
39 |
40 | 虽然很多人安装完 CentOS/Fedora 之后做的头几件事之一,就是把 SELinux 关掉。它可能会导致一些奇怪的权限问题,或是让某些程序运行失败。但是我们不建议在需要强安全性的环境中关闭 SELinux。[清华大学 TUNA 协会](https://tuna.moe)曾在活动中做过 [SELinux 的使用介绍](https://tuna.moe/event/2020/selinux-introduction/),希望进一步了解 SELinux 的同学可以在线观看其[活动录像](https://mirrors.tuna.tsinghua.edu.cn/tuna/tunight/2020-06-27-selinux-introduction/)。
41 |
42 | 在 Ubuntu 等发行版中,使用的类似于 SELinux 的安全模块是 AppArmor。AppArmor 使用文件路径(而非标签)进行管理,使用相较于 SELinux 简单一些。
43 |
44 | ## Arch Linux {#archlinux}
45 |
46 | ### Arch Wiki {#archwiki}
47 |
48 | [Arch Wiki](https://wiki.archlinux.org/) 是安装和使用 Arch Linux 必读的资料,内容非常详细。即使不是 Arch Linux 的使用者,Wiki 的内容也非常有参考价值。
49 |
50 | ### 软件包管理 {#arch-software-management}
51 |
52 | Arch 使用的默认软件包管理器是 `pacman`。以下是一些常用的命令。
53 |
54 | ```console
55 | $ sudo pacman -Syu # 更新系统所有软件包
56 | $ sudo pacman -S firefox # 安装 Firefox
57 | $ sudo pacman -Rs chromium # 卸载 Chromium 和它的所有依赖
58 | $ pacman -Ss audacity # 搜索 Audacity
59 | ```
60 |
61 | #### 手动介入 (manual intervention):更新失败时的处理方式 {#arch-manual-intervention}
62 |
63 | 由于其滚动发行的特性,在更新时可能会出现安装错误的情况。一般来说,你需要关注 Arch 的主页新闻 (),当有软件包需要手动介入更新时,可以看到错误提示和解决方法。
64 |
65 | #### AUR {#aur}
66 |
67 | [AUR (Arch User Repository)](https://aur.archlinux.org) 由 Arch 用户维护,是 Arch Linux 的一大特色。其上包含了大量的程序可供安装。用户可以对软件包评论、投票,与各自的维护者交流。
68 |
69 | 有很多的程序(统称为 AUR Helper)可以帮助从 AUR 上下载安装包,例如 `yay` 和 `paru`。
70 |
71 | ## openSUSE {#opensuse}
72 |
73 | 提示:关于 openSUSE 有一份不错的手册 [opensuse-guide.org](https://opensuse-guide.org/)([中文翻译](https://opensuse-guide.ustclug.org))。
74 |
75 | ### 发行版本 {#opensuse-release}
76 |
77 | openSUSE 最主要使用的发行版本为 Leap 和 Tumbleweed。
78 |
79 | openSUSE Leap 是定期发布的常规版本,截至 2022 年初,最新的版本为 15.3。而 openSUSE Tumbleweed(又名「风滚草」)是滚动更新的,类似于 Arch Linux。
80 |
81 | !!! warning "openSUSE Leap 15.x 比 openSUSE Leap 42.x 更新。"
82 |
83 | ### 软件包管理 {#suse-software-management}
84 |
85 | openSUSE 使用 RPM 作为其软件包格式,但是与 Fedora、CentOS 等不同的是,其软件包管理器为 ZYpp(Zen / YaST Packages Patches Patterns Products)。用户可以在命令行中使用 `zypper` 进行安装、卸载、升级软件等操作。
86 |
87 | 以下是一些常用的命令:
88 |
89 | ```console
90 | $ sudo zypper update # 更新系统所有软件包
91 | $ sudo zypper install firefox # 安装 Firefox
92 | $ sudo zypper remove chromium # 卸载 Chromium 和它的所有依赖
93 | $ zypper search audacity # 搜索 Audacity
94 | ```
95 |
96 | ### 系统管理工具 YaST {#yast}
97 |
98 | YaST 工具是 openSUSE 的一大特色。它提供了图形化的界面,可以帮助系统管理员完成各种常见操作。
99 |
100 | 
101 |
102 | YaST 控制中心截图
103 | {: .caption }
104 |
105 | ### Open Build Service (OBS) {#obs}
106 |
107 | [OBS](https://openbuildservice.org/) 源于 openSUSE 为社区打包软件所提供的服务,目前支持为各大 Linux 发行版提供打包服务。用户也可以自己搭建 OBS 服务。
108 |
109 | ### Btrfs 与系统集成 {#suse-btrfs}
110 |
111 | Btrfs 文件系统是 openSUSE 在安装时为根分区选择的默认文件系统。借助 Btrfs 的快照特性,openSUSE 的 Snapper 可以实现在软件管理操作前后创建快照,允许用户回滚到之前的状态,或者从快照启动系统。
112 |
113 | ## Gentoo {#gentoo}
114 |
115 | ### 软件包管理 {#gentoo-software-management}
116 |
117 | Gentoo 的软件包管理器是 Portage。其对应最常用的 CLI 工具是 `emerge`。
118 |
119 | 以下是一些常用的命令:
120 |
121 | ```console
122 | $ sudo emerge --sync # 更新软件包索引
123 | $ sudo emerge --update --ask @world # 更新已安装的程序(不包含依赖)
124 | $ sudo emerge -a firefox # 安装 Firefox
125 | $ sudo emerge --unmerge chromium # 卸载 Chromium 和它的所有依赖
126 | $ emerge --search audacity # 搜索名字中含 audacity 的包
127 | ```
128 |
129 | 由于 Gentoo 以编译安装为主,和其他 Linux 发行版不同,用户可以指定在安装时需要软件的哪些特性。例如,服务器需要的软件特性肯定与桌面不同,一些桌面上必须的功能在服务器上并不需要,反之亦然。用户可以通过修改 USE 标志来为软件包添加或删除特性。
130 |
131 | 关于 USE 标志的使用可以参考 [Gentoo 官方手册中的简要介绍](https://wiki.gentoo.org/wiki/Handbook:AMD64/Working/USE/zh-cn)。
132 |
133 | ### OpenRC {#openrc}
134 |
135 | 尽管 Systemd 已经成为了 Linux 发行版主流选择的 init,OpenRC 仍然是 Gentoo 默认的 init(关于 init 的简介,可参考[第四章](../Ch04/index.md))。
136 |
137 | [^1]:
138 |
--------------------------------------------------------------------------------
/docs/Appendix/images/yast.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Appendix/images/yast.png
--------------------------------------------------------------------------------
/docs/Appendix/markdown.md:
--------------------------------------------------------------------------------
1 | # Markdown 使用教程
2 |
3 | Markdown 是一种轻量级的标记语言,可用于在纯文本文档中添加格式化元素。Markdown 由 John Gruber 于 2004 年创建,如今已成为世界上最受欢迎的标记语言之一。Linux 101 也是基于 Markdown 编写的。
4 |
5 | ???+ question "为什么推荐使用 Markdown?"
6 |
7 | 当你可以通过按下界面中的按钮来设置文本格式时,为什么还要使用 Markdown 来书写呢?使用 Markdown 而不是 Word 类编辑器的原因有:
8 |
9 | - **Markdown 无处不在。** Stack Overflow、CSDN、掘金、简书、GitBook、有道云笔记、V2EX、光谷社区等。主流的代码托管平台,如 GitHub、GitLab、BitBucket、Coding、Gitee 等等,都支持 Markdown 语法,很多开源项目的 README、开发文档、帮助文档、Wiki 等都用 Markdown 写作。
10 |
11 | - **Markdown 是纯文本可移植的。** 几乎可以使用任何应用程序打开包含 Markdown 格式的文本文件。如果你不喜欢当前使用的 Markdown 应用程序了,则可以将 Markdown 文件导入另一个 Markdown 应用程序中。这与 Microsoft Word 等文字处理应用程序形成了鲜明的对比,Microsoft Word 将你的内容锁定在专有文件格式中。
12 |
13 | - **Markdown 是独立于平台的。** 你可以在运行任何操作系统的任何设备上创建 Markdown 格式的文本。
14 |
15 | - **Markdown 能适应未来的变化。** 即使你正在使用的应用程序将来会在某个时候不能使用了,你仍然可以使用文本编辑器读取 Markdown 格式的文本。当涉及需要无限期保存的书籍、大学论文和其他里程碑式的文件时,这是一个重要的考虑因素。
16 |
17 | 使用 Markdown 与使用 Word 类编辑器不同。在 Word 之类的应用程序中,我们通过单击按钮以设置文本的格式,并且更改的效果立即可见。而 Markdown 与此不同,当你在编写 Markdown 格式的文件时,需要在文本中添加 Markdown 语法,以指示哪些单词和短语看起来应该有所不同。
18 |
19 | 例如,要表示标题,只须在短语前面添加一个井号 `#` 即可;或者要加粗一个短语,只须在短语前后各加两个星号 `*` 即可(例如,**this text is bold**)。你可能需要一段时间才能习惯 Markdown 语法,尤其是如果已经习惯了所见即所得的文本编辑程序。
20 |
21 | 接下来,我们将简单介绍 Markdown 中的部分语法知识。
22 |
23 | ## Markdown 常用语法
24 |
25 | ### 标题
26 |
27 | Markdown 通过在行首插入 1 到 6 个 `#` 实现标题效果,分别对应 1 到 6 级标题。
28 |
29 | !!! example "Markdown 的标题"
30 |
31 | ```markdown
32 | # this is H1
33 | ## this is H2
34 | ###### this is H6
35 | ```
36 |
37 | this is H1
38 | this is H2
39 | this is H6
40 |
41 | ### 段落
42 |
43 | Markdown 使用空白行将一行或多行文本进行分隔,从而实现段落的效果。
44 |
45 | !!! example "Markdown 的段落"
46 |
47 | ```markdown
48 | 中国科学技术大学是中国科学院所属的一所以前沿科学和高新技术为主,兼有医学、特色管理和人文学科的理工科大学。
49 |
50 | 学校现有 31 个学院(学部),含 8 个科教融合学院;设有苏州高等研究院、上海研究院、北京研究院、先进技术研究院、
51 | 国际金融研究院、附属第一医院(安徽省立医院)。
52 | ```
53 |
54 | 中国科学技术大学是中国科学院所属的一所以前沿科学和高新技术为主,兼有医学、特色管理和人文学科的理工科大学。
55 |
56 | 学校现有 31 个学院(学部),含 8 个科教融合学院;设有苏州高等研究院、上海研究院、北京研究院、先进技术研究院、国际金融研究院、附属第一医院(安徽省立医院)。
57 |
58 | 注意:请**不要**用空格(spaces)或制表符(tabs)缩进段落。
59 |
60 | ### 字体
61 |
62 | Markdown 使用星号 `*` 和下划线 `_` 作为标记强调字词的符号,你可以随意选择喜欢的样式,但用什么符号开启标签,就要用什么符号结束。
63 |
64 | !!! example "Markdown 的字体"
65 |
66 | **这是加粗**(`**这是加粗**`)
67 |
68 | __这也是加粗__(`__这也是加粗__`)
69 |
70 | *这是倾斜*(`*这是倾斜*`)
71 |
72 | _这也是倾斜_(`_这也是倾斜_`)
73 |
74 | ***这是加粗倾斜***(`***这是加粗倾斜***`)
75 |
76 | ~~这是加删除线~~(`~~这是加删除线~~`)
77 |
78 | 注意:强调也可以直接插在文字中间,但是如果你的 \* 和 \_ 两边都有空白的话,它们就只会被当成普通的符号。如果要在文字前后直接插入普通的星号或底线,你可以用反斜线 `\`。例如:USTC_VLAB(`USTC\_VLAB`)
79 |
80 | ### 列表
81 |
82 | Markdown 支持有序列表和无序列表。无序列表使用星号 `*`、加号 `+` 或是减号 `-` 作为列表标记,符号与内容之间需要存在一个空格。
83 |
84 | !!! example "Markdown 无序列表"
85 |
86 | ```markdown
87 | - 列表内容
88 | + 列表内容
89 | * 列表内容
90 | ```
91 |
92 | - 列表内容
93 | + 列表内容
94 | * 列表内容
95 |
96 | 有序列表则使用数字接着一个英文句点作为标记,序号跟内容之间要有空格。
97 |
98 | !!! tip "例子:Markdown 有序列表"
99 |
100 | ```markdown
101 | 1. 列表内容
102 | 2. 列表内容
103 | 3. 列表内容
104 | ```
105 |
106 | 1. 列表内容
107 | 2. 列表内容
108 | 3. 列表内容
109 |
110 | 列表可以嵌套,只需要加上正确的缩进即可。
111 |
112 | !!! example "Markdown 列表嵌套"
113 |
114 | ```markdown
115 | 1. First item
116 | 2. Second item
117 | 3. Third item
118 | - Indented item
119 | - Indented item
120 | 4. Fourth item
121 | ```
122 |
123 | 1. First item
124 | 2. Second item
125 | 3. Third item
126 | - Indented item
127 | - Indented item
128 | 4. Fourth item
129 |
130 | ### 引用
131 |
132 | Markdown 通过在引用的文字前加 `>` 实现引用。引用的区块内也可以使用其他的 Markdown 语法,包括标题、列表、代码区块等。
133 |
134 | !!! example "Markdown 的引用"
135 |
136 | ```markdown
137 | > - Revenue was off the chart.
138 | > - Profits were higher than ever.
139 | >
140 | > *Everything* is going according to **plan**.
141 | ```
142 |
143 | > - Revenue was off the chart.
144 | > - Profits were higher than ever.
145 | >
146 | > *Everything* is going according to **plan**.
147 |
148 | ### 代码
149 |
150 | 在 Markdown 中加入代码块有两种方式:
151 |
152 | 1. 缩进 4 个空格或是 1 个制表符;
153 | 2. **推荐**:使用三个反引号 ` ``` ` 包裹起来,且两边的反引号单独占一行。
154 |
155 | !!! example "Markdown 代码块"
156 |
157 | ````markdown
158 | 这是一个普通段落
159 |
160 | ```
161 | 这是一个代码块。
162 | ```
163 | ````
164 |
165 | 这是一个普通段落
166 |
167 | ```
168 | 这是一个代码块
169 | ```
170 |
171 | 除了代码块,Markdown 还支持使用一个反引号 `` ` `` 包裹的句内代码。例如:`int i`(`` `int i` ``)。
172 |
173 | ### 图片
174 |
175 | Markdown 使用一种和链接很相似的语法来标记图片,看起来像是:
176 |
177 | ```markdown
178 | 
179 |
180 | 
181 | ```
182 |
183 | ???+ info "图片的路径问题"
184 |
185 | 如何选择图片的路径呢?如果使用本地路径插入本地图片,这样就不灵活不好分享,本地图片的路径更改或丢失都会造成 md 文档无法找到图片;如果插入网络图片,则非常依赖网络,本地图片还需要先上传到服务器上再插入。
186 |
187 | 目前常见的解决方法是:将本地图片上传到 GitHub,或者在当前文档同目录或仓库根目录下新建 src 或 figs 文件夹,将需要引用的图片上传至其中,在文档中使用相对路径引用即可。例如,下面这张图片就位于 `/Ch01/images` 目录下。
188 |
189 | { width="300" }
190 |
191 | 受篇幅所限,我们仅介绍 Markdown 的一些常用语法,想要了解更多语法知识的同学可以自行查找有关资料。
192 |
193 | ## Markdown 编辑器
194 |
195 | 我们推荐使用 [Typora](https://typoraio.cn/) 进行 Markdown 的编写,也可以使用 VSCode 配置 Markdown 插件进行编写。下面是一些推荐的 VSCode 插件:
196 |
197 | - [Markdown All in One](https://marketplace.visualstudio.com/items?itemName=yzhang.markdown-all-in-one)(`yzhang.markdown-all-in-one`)
198 | - [Markdown Preview Enhanced](https://marketplace.visualstudio.com/items?itemName=shd101wyy.markdown-preview-enhanced)(`shd101wyy.markdown-preview-enhanced`)
199 | - [Markdown Preview GitHub Styling](https://marketplace.visualstudio.com/items?itemName=bierner.markdown-preview-github-styles)(`bierner.markdown-preview-github-styles`)
200 |
201 | 你可以根据需要进行安装。
202 |
203 | ## 参考资料
204 |
205 | - [Markdown 官方教程](https://markdown.com.cn/)
206 | - [知乎:使用 vscode 开始 Markdown 写作之旅](https://zhuanlan.zhihu.com/p/56943330/)
207 | - [USTC OSH-2023 课程主页](https://osh-2023.github.io/lab0/markdown/)
208 |
--------------------------------------------------------------------------------
/docs/Ch01/images/Android-10-Native.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch01/images/Android-10-Native.png
--------------------------------------------------------------------------------
/docs/Ch01/images/Android-Stack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch01/images/Android-Stack.png
--------------------------------------------------------------------------------
/docs/Ch01/images/Android-TV.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch01/images/Android-TV.png
--------------------------------------------------------------------------------
/docs/Ch01/images/Arch-Linux-Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch01/images/Arch-Linux-Logo.png
--------------------------------------------------------------------------------
/docs/Ch01/images/CentOS-Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch01/images/CentOS-Logo.png
--------------------------------------------------------------------------------
/docs/Ch01/images/Copyleft.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch01/images/Copyleft.png
--------------------------------------------------------------------------------
/docs/Ch01/images/Debian-Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch01/images/Debian-Logo.png
--------------------------------------------------------------------------------
/docs/Ch01/images/Fedora-Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch01/images/Fedora-Logo.png
--------------------------------------------------------------------------------
/docs/Ch01/images/LUG@USTC-Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch01/images/LUG@USTC-Logo.png
--------------------------------------------------------------------------------
/docs/Ch01/images/Manjaro-Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch01/images/Manjaro-Logo.png
--------------------------------------------------------------------------------
/docs/Ch01/images/Performance-Era.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch01/images/Performance-Era.jpg
--------------------------------------------------------------------------------
/docs/Ch01/images/Red-Hat-Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch01/images/Red-Hat-Logo.png
--------------------------------------------------------------------------------
/docs/Ch01/images/Ubuntu-Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch01/images/Ubuntu-Logo.png
--------------------------------------------------------------------------------
/docs/Ch01/images/VWP-Xubuntu-32bit-Desktop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch01/images/VWP-Xubuntu-32bit-Desktop.png
--------------------------------------------------------------------------------
/docs/Ch01/images/VWP-Xubuntu-32bit-Login.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch01/images/VWP-Xubuntu-32bit-Login.png
--------------------------------------------------------------------------------
/docs/Ch01/images/VirtualBox-Create-VM.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch01/images/VirtualBox-Create-VM.jpg
--------------------------------------------------------------------------------
/docs/Ch01/images/VirtualBox-import.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch01/images/VirtualBox-import.jpg
--------------------------------------------------------------------------------
/docs/Ch01/images/Windows-Server.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch01/images/Windows-Server.png
--------------------------------------------------------------------------------
/docs/Ch01/images/Xubuntu-Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch01/images/Xubuntu-Logo.png
--------------------------------------------------------------------------------
/docs/Ch01/images/applesilicon_vmware/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch01/images/applesilicon_vmware/1.png
--------------------------------------------------------------------------------
/docs/Ch01/images/applesilicon_vmware/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch01/images/applesilicon_vmware/2.png
--------------------------------------------------------------------------------
/docs/Ch01/images/applesilicon_vmware/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch01/images/applesilicon_vmware/3.png
--------------------------------------------------------------------------------
/docs/Ch01/images/applesilicon_vmware/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch01/images/applesilicon_vmware/4.png
--------------------------------------------------------------------------------
/docs/Ch01/images/applesilicon_vmware/6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch01/images/applesilicon_vmware/6.png
--------------------------------------------------------------------------------
/docs/Ch01/images/applesilicon_vmware/7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch01/images/applesilicon_vmware/7.png
--------------------------------------------------------------------------------
/docs/Ch01/solution.md:
--------------------------------------------------------------------------------
1 | ---
2 | icon: material/tooltip-question
3 | ---
4 |
5 | # 思考题解答 {#solution}
6 |
7 | ## 计算机性能的增长 {#performance-increase}
8 |
9 | !!! tip "提示"
10 |
11 | 本题在书中没有答案,并且计算机性能的时代也不存在十分明确的分界线。所以本题可算作一个开放题,并且鼓励各位读者自行查阅资料来形成自己的答案。
12 |
13 | 本题与计算机的体系结构比较相关,建议读者查阅的时候可以以此为关键词。
14 |
15 | ??? info "解答"
16 |
17 | **(1)**
18 |
19 | 计算机的指数增长时代大致可以认为由 2 个阶段构成:
20 |
21 | * 复杂指令集阶段:这个阶段是大规模集成电路阶段的开始。在这个阶段,得益于电路的集成度大幅度提升,计算机的性能也成指数级增长。这个阶段大致的时间范围是 1978 - 1986 年,平均每年性能提升 22%。
22 |
23 | * 精简指令集阶段:随着时间的推移,人们发现许多指令通常都用不到,但它们的存在让处理器不得不采用更复杂的结构,因此研究员开始提倡精简之前的指令集架构以简化处理器。在使用了精简后的指令集架构后,计算机的性能相较之前有了飞跃般的提升(最初在 IBM 360 指令集架构上是 3 倍)。同时,集成电路的密度依然在继续增加,且在这个阶段开发人员通过大力改进“指令级并行”以提升性能,因此此时计算机的性能以更快的速度增长。这个阶段大致的时间范围是 1986 - 2003 年,平均每年性能提升 52%,是计算机科技的黄金时代。
24 |
25 | **(2)**
26 |
27 | 计算机指数增长的黄金年代之所以能长期保持,主要还是因为集成电路的密度可以稳定大幅度提升。罗伯特·丹纳德(Robert Dennard)归纳了一个规律,被称之为丹纳德标度(Dennard Scaling):“在晶体管的密度增加的时候,单个晶体管的功耗会下降,这样下来可以保持晶体管的功耗密度(每单位面积的功耗)不变”。根据这条规律,在晶体管密度增加的同时,计算机性能会提升(正比例于晶体管数目),但功耗不变,因此计算机的能量利用率会越来越高。
28 |
29 | 不过,这条规律没有考虑到一些重要的问题。一个问题是晶体管的“漏电电流”(微安级别),即晶体管在工作时会有轻微漏电,这些漏电会导致处理器发热,如果不加以控制会造成严重的散热问题。当然,可以通过降低电压的方法来减少散热,但此时也出现了第二个问题,即是晶体管的“阈值电压”(大约是 0.7 ~ 0.8 伏),可以理解成最低工作电压,低于此电压晶体管不再工作。这两个问题导致了晶体管存在一个不可跨越的最低功率(以及最低发热功率),从而使得丹纳德标度失效。进一步增加密度将需要更多能耗而不再恒定,也会导致散热问题。因此,为了避免这个问题,芯片厂商研究了多核处理器来达到并行的效果,从而进入了多核时代。
30 |
31 | * 后丹纳德标度阶段:这是多核时代的第一个阶段,这个阶段计算机性能提升的主要因素是采用了多核处理器,通过并行的方式提升计算机的性能。这个阶段大致的时间范围是 2003 - 2011 年,平均每年性能提升 23%,相比之前的 52% 是一个巨大的跌落。
32 |
33 | **(3)**
34 |
35 | 在多核时代,操作系统显然需要适应多核处理器,而其中一个最重要的能力就是进行并行任务调度,即将任务按合适的方式交付给不同的核来同时完成以尽可能充分利用并行的优势。
36 |
37 | **题外话**
38 |
39 | 然而,核并不是越多越好的,因为一个任务总会有一些先后次序是不能颠倒的,所以这些部分就必须串行运行。就算假设一个任务能并行的部分可以被完美地平均切分而并行执行,剩余的串行部分也无法利用到并行的任何收益(而且往往过度的并行反而降低效率),因此增加更多核的收益会越来越低。这种规律被称之为阿姆达尔定律(Amdahl's Law),核的数目越多,越受到阿姆达尔定律的限制。
40 |
41 | * 阿姆达尔定律阶段:这是多核时代的第二个阶段,这个阶段标志着多核所带来的性能提升已经越来越不明显了。这个阶段大致的时间范围是 2011 - 2015 年,平均每年性能提升只有 12%。
42 |
43 | 在 2015 年以后,平均每年的性能提升更是暴跌至 3%,似乎大规模集成电路计算机的性能提升已经快到了尽头,因此又被称为终结阶段。
44 |
45 | 
46 |
47 | 大规模集成电路计算机性能的五个阶段
48 | {: .caption }
49 |
50 | ## 免费软件和自由软件 {#free-vs-free}
51 |
52 | !!! tip "提示"
53 |
54 | 要回答本题,需要明确了解“自由软件”的概念。依据自由软件基金会的定义,自由软件是一类可以不受限制地自由使用、复制、研究、修改和分发的,尊重用户自由的软件。[^1]可以注意到这里的定义和免费没有什么关系。
55 |
56 | 为了更好地理解这两种定义之间的关系,最好从正反两面举出几个实际例子。
57 |
58 | ??? info "解答"
59 |
60 | 免费软件并不一定自由,因为免费的软件可能只是软件的所有者提供了免费许可,但并不一定也会同时允许用户自由地去研究、修改和分发。一些反例诸如:
61 |
62 | * QQ: 腾讯公司推出的免费即时通讯软件,在中国十分主流,不过显然腾讯公司从来没公开过 QQ 的源代码,也不允许用户去自行修改它。
63 |
64 | * Adobe Reader:Adobe 公司推出的免费 PDF 阅读器,但该公司拥有该软件的所有权,并且此软件也不开源,不允许用户去修改。
65 |
66 | * WPS Office:金山办公旗下的一系列办公软件的组合,它是免费的,但同样不允许用户研究和修改。
67 |
68 | 支持这个命题的例子不胜枚举,比如许多收费的专业软件的个人版、教育版或者社区版等都是免费但不自由的软件。
69 |
70 | 自由软件也并不一定免费。虽然对于不少初接触的读者来说可能稍微难以想象:因为自由软件一般都是遵循自由软件协议开源的,谁都可以拿出去编译成自己的副本,似乎没有收费的余地。其实不然,一是自由软件的定义并不妨碍其收费,因此理论上想收费就可以收费;二是自由软件的发行商可以为顾客提供专业的技术服务,此时相当于买自由软件的这笔钱可以购买到专业技术支持。一个十分典型的例子就是本章中提到的 Red Hat 公司推出的 RHEL,它是收费的,但遵循 GPL 开源。使用 RHEL 源代码编译成的免费版本(再去掉 RHEL 本身包含的闭源软件和 Red Hat 商标信息)就是本章正文中提到的 CentOS 了。
71 |
72 | ## 著作传 {#copyleft}
73 |
74 | !!! tip "提示"
75 |
76 | 本题实质上仅要求读者了解“著作传”的定义和它与其它两种许可方式的区别,难度并不大。不过,本章正文中没有给出著作传的完整定义,因此本题鼓励读者自行查阅在线资料来掌握这个知识。
77 |
78 | ??? info "解答"
79 |
80 | 著作传的概念源于自由软件运动,是一种利用现有的“著作权”的体制来保障用户软件自由使用权利的许可方式。通常来说,著作传保证了任何用户都可以自由地使用、复制、修改和传播所许可的软件,不过基于这个软件的复制传播和修改后的再分发通常还必须以著作传的方式发布,即后续的用户也能享受到同等的自由。
81 |
82 | 由上面的解释可见,著作传许可比通常的著作权提供的使用许可要宽容、自由得多,因此可能部分初次接触这个概念的读者会觉得这个许可方式和直接放出去让大家随便使用没什么区别,然而这种想法是不对的。事实上,著作传和著作权、公有领域的关系和区别有以下几个显著的要点:
83 |
84 | * 著作传虽然允许软件可以以很自由的方式给用户使用,但用户依然需要遵守著作传的许可,而不是像公有领域的产品一样可以完全随心所欲。著作传许可的一个典型特性就是要求它的传播和衍生还是要继续采用著作传许可,而不可以申请自己的著作权,也不能重新赋予新的许可。这种方法保证了这个软件的权利完全由人类共同体充分享受,不过这种方式也得到了一些反对声音,因为这种许可的传播方式看起来太像是病毒传染。
85 |
86 | * 著作传是利用了现有的著作权体系设立的,这说明著作传许可虽然自由,但实际上也可以认为是对软件的一种保护,是另一种“著作权”。这也说明了著作传理论是一种受到法律保护的许可(如果当地法律兼容的话),而不是某种大家自发遵守的倡议。不过和普通的著作权不同,著作传许可面向用户下放了许多权利,这是通常的著作权许可不会做的。
87 |
88 | ## 引用来源 {#references .no-underline}
89 |
90 | [^1]: 定义来自维基百科条目:[自由软件](https://zh.wikipedia.org/wiki/%E8%87%AA%E7%94%B1%E8%BD%AF%E4%BB%B6)。
91 |
--------------------------------------------------------------------------------
/docs/Ch02/images/Plasma-desktop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch02/images/Plasma-desktop.png
--------------------------------------------------------------------------------
/docs/Ch02/images/Plasma-themes-store.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch02/images/Plasma-themes-store.png
--------------------------------------------------------------------------------
/docs/Ch02/images/Xfce-Autostart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch02/images/Xfce-Autostart.png
--------------------------------------------------------------------------------
/docs/Ch02/images/Xfce-FM-list.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch02/images/Xfce-FM-list.png
--------------------------------------------------------------------------------
/docs/Ch02/images/Xfce-FM.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch02/images/Xfce-FM.png
--------------------------------------------------------------------------------
/docs/Ch02/images/Xfce-WM-keyboard.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch02/images/Xfce-WM-keyboard.png
--------------------------------------------------------------------------------
/docs/Ch02/images/Xfce-WM-style-installation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch02/images/Xfce-WM-style-installation.png
--------------------------------------------------------------------------------
/docs/Ch02/images/Xfce-WM-style.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch02/images/Xfce-WM-style.png
--------------------------------------------------------------------------------
/docs/Ch02/images/Xfce-appearance-fonts.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch02/images/Xfce-appearance-fonts.png
--------------------------------------------------------------------------------
/docs/Ch02/images/Xfce-appearance-icons.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch02/images/Xfce-appearance-icons.png
--------------------------------------------------------------------------------
/docs/Ch02/images/Xfce-menu-applocations.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch02/images/Xfce-menu-applocations.png
--------------------------------------------------------------------------------
/docs/Ch02/images/Xfce-menu-workspaces.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch02/images/Xfce-menu-workspaces.png
--------------------------------------------------------------------------------
/docs/Ch02/images/Xfce-panel-display.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch02/images/Xfce-panel-display.png
--------------------------------------------------------------------------------
/docs/Ch02/images/Xfce-panel-items.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch02/images/Xfce-panel-items.png
--------------------------------------------------------------------------------
/docs/Ch02/images/Xfce-sessions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch02/images/Xfce-sessions.png
--------------------------------------------------------------------------------
/docs/Ch02/images/Xfce-settings-FM.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch02/images/Xfce-settings-FM.png
--------------------------------------------------------------------------------
/docs/Ch02/images/Xfce-settings-appearance.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch02/images/Xfce-settings-appearance.png
--------------------------------------------------------------------------------
/docs/Ch02/images/Xfce-settings-background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch02/images/Xfce-settings-background.png
--------------------------------------------------------------------------------
/docs/Ch02/images/Xfce-settings-desktop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch02/images/Xfce-settings-desktop.png
--------------------------------------------------------------------------------
/docs/Ch02/images/Xfce-settings-icons.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch02/images/Xfce-settings-icons.png
--------------------------------------------------------------------------------
/docs/Ch02/images/Xfce-settings-menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch02/images/Xfce-settings-menu.png
--------------------------------------------------------------------------------
/docs/Ch02/images/Xfce-settings-position.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch02/images/Xfce-settings-position.png
--------------------------------------------------------------------------------
/docs/Ch02/images/Xfce-settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch02/images/Xfce-settings.png
--------------------------------------------------------------------------------
/docs/Ch02/images/Xfce-style-installation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch02/images/Xfce-style-installation.png
--------------------------------------------------------------------------------
/docs/Ch02/images/Xfce-style-installation_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch02/images/Xfce-style-installation_2.png
--------------------------------------------------------------------------------
/docs/Ch02/images/Xfce-terminal-pos.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch02/images/Xfce-terminal-pos.png
--------------------------------------------------------------------------------
/docs/Ch02/images/Xfce-terminal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch02/images/Xfce-terminal.png
--------------------------------------------------------------------------------
/docs/Ch02/images/Xfce-top-panel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch02/images/Xfce-top-panel.png
--------------------------------------------------------------------------------
/docs/Ch02/images/caffeine-folder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch02/images/caffeine-folder.png
--------------------------------------------------------------------------------
/docs/Ch02/images/caffeine.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch02/images/caffeine.png
--------------------------------------------------------------------------------
/docs/Ch02/images/conky-manager.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch02/images/conky-manager.png
--------------------------------------------------------------------------------
/docs/Ch02/images/conky.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch02/images/conky.png
--------------------------------------------------------------------------------
/docs/Ch02/images/download-theme.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch02/images/download-theme.png
--------------------------------------------------------------------------------
/docs/Ch02/images/extensions-management.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch02/images/extensions-management.png
--------------------------------------------------------------------------------
/docs/Ch02/images/gnome-desktop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch02/images/gnome-desktop.png
--------------------------------------------------------------------------------
/docs/Ch02/images/gnome-extensions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch02/images/gnome-extensions.png
--------------------------------------------------------------------------------
/docs/Ch02/images/gnome-tweak-tool.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch02/images/gnome-tweak-tool.png
--------------------------------------------------------------------------------
/docs/Ch02/images/jekyll-installation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch02/images/jekyll-installation.png
--------------------------------------------------------------------------------
/docs/Ch02/images/metadata.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch02/images/metadata.png
--------------------------------------------------------------------------------
/docs/Ch02/images/ocs-url-download.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch02/images/ocs-url-download.png
--------------------------------------------------------------------------------
/docs/Ch02/images/themes-download-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch02/images/themes-download-icon.png
--------------------------------------------------------------------------------
/docs/Ch02/images/wordpress-installation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch02/images/wordpress-installation.png
--------------------------------------------------------------------------------
/docs/Ch02/images/xfce-look-mainpage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch02/images/xfce-look-mainpage.png
--------------------------------------------------------------------------------
/docs/Ch02/images/xfce-look-theme.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch02/images/xfce-look-theme.png
--------------------------------------------------------------------------------
/docs/Ch02/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | icon: fontawesome/solid/circle-user
3 | ---
4 |
5 | # 个性化配置与建站体验
6 |
7 | !!! success "本文已完稿并通过审阅,是正式版本。"
8 |
9 | !!! abstract "导言"
10 |
11 | Linux 是一个可以高度个性化定制的系统,当然也包括界面的个性化,因此本章节将带大家解决这些问题:
12 |
13 | * 如何选择并安装桌面环境
14 | * 如何打造自己独特的桌面
15 | * 如何配置美化命令行终端
16 | * 如何简单快速地搭建网站
17 |
18 | ## 桌面环境 {#desktop-environment}
19 |
20 | Linux 本身并不包括图形界面。没有图形界面的 Linux 系统,只能通过命令行来管理。
21 |
22 | Linux 中的桌面环境也是一个程序,它和内核不是绑定的,两者的开发也不是同步的;给不带界面的 Linux 系统安装上一个桌面环境,你就能看到各种漂亮的窗口,并能用鼠标点击它们了。
23 |
24 | ### 桌面环境的选择
25 |
26 | Linux 的桌面环境可不止一种,下面介绍几个流行的桌面环境。
27 |
28 | KDE Plasma
29 |
30 | : KDE 软件社区提供的 Plasma Linux 桌面环境是最可定制的图形桌面环境之一。此功能丰富且功能强大的桌面环境还拥有许多小部件。它允许用户自由地添加桌面的控制面板。
31 | [Plasma 官方网站](https://www.kde.org/plasma-desktop)
32 |
33 | GNOME
34 |
35 | : GNOME 的设计目标是为用户提供简单性,易于访问性和可靠性。正因为这些,GNOME 得到了普及。
36 | [GNOME 官网](https://www.gnome.org/)
37 |
38 | Xfce
39 |
40 | : Xfce 是一款快速、轻量,界面美观和对用户友好的桌面环境。
41 | [Xfce 官网](https://www.xfce.org/)
42 |
43 | !!! info "提示"
44 |
45 | 除了上面的桌面环境,常见的还有 Cinnamon, Unity 等,同学们可以自行了解。
46 |
47 | !!! tip "提示"
48 |
49 | 如果你想要安装其它桌面系统(如 GNOME)或者你的系统未预装好桌面环境,可以参阅拓展阅读的内容。
50 |
51 | 本次教学所用的系统 Xubuntu 预先安装了 Xfce 桌面环境,因此本章将主要围绕 Xfce 的个性化来开展。实际上无论是哪一款桌面系统,个性化的方式都大同小异。
52 |
53 | ### 常用外观个性化 {#appearance-settings}
54 |
55 | 大部分 Linux 桌面环境个性化功能并不比 Windows 差。
56 |
57 | 我们直接打开 Xfce 中的设置管理器,如图所示。
58 |
59 | 
60 |
61 | 图 1. 设置管理器的位置
62 | {: .caption }
63 |
64 | 这里最常见的设置,都可以找得到。
65 |
66 | 
67 |
68 | 图 2. 设置管理器
69 | {: .caption }
70 |
71 | #### 桌面 {#desktop}
72 |
73 | xfdesktop 桌面管理器是 Xfce 中的一个主要模块,它负责在桌面上设置背景图像/颜色和绘制图标。当你分别用鼠标右键或鼠标中键单击桌面时,它可以显示应用程序菜单和所有正在运行的应用程序的列表。
74 |
75 | ##### 背景 {#desktop-background}
76 |
77 | 
78 |
79 | 图 3. 桌面背景首选项
80 | {: .caption }
81 |
82 | 背景设置是对壁纸的更改和个性化。
83 |
84 | 下表是一些配置的补充说明。
85 |
86 | | 字段 | 功能 |
87 | | -------- | ---------------------------------------------------------------------------- |
88 | | 目录 | 存放壁纸的目录。 |
89 | | 样式 | 壁纸的显示样式、包括居中、平铺等。 |
90 | | 颜色 | 用于填充背景空缺部分的颜色,可以设置纯色、渐变、透明。 |
91 | | 修改背景 | 如果一个位置包含多张图像,则 Xfce 允许你通过循环浏览可用图像来自动更改背景。 |
92 |
93 | ##### 菜单 {#desktop-menus}
94 |
95 | 
96 |
97 | 图 4. 桌面菜单首选项
98 | {: .caption }
99 |
100 | Xfce 允许用户自定义右键菜单和中键菜单的行为。这里可以对在桌面右键和中键的菜单进行设置。
101 |
102 | 若「在桌面上右击时包含应用程序菜单」选项被选中,则在桌面右键时会显示下面的菜单,用于快速打开应用程序。
103 |
104 | 
105 |
106 | 图 5. 桌面应用程序菜单
107 | {: .caption }
108 |
109 | 若「在桌面上中击时显示窗口列表菜单」选项被选中时,中击桌面可以弹出工作区的菜单,可以显示所有工作区正在运行的应用程序。
110 |
111 | 
112 |
113 | 图 6. 桌面工作区菜单
114 | {: .caption }
115 |
116 | ##### 图标 {#desktop-icons}
117 |
118 | 
119 |
120 | 图 7. 桌面图标首选项
121 | {: .caption }
122 |
123 | Xfce 允许用户绘制桌面图标并且设置其外观。
124 |
125 | 在图标类型项,你可以下拉菜单选择「无」来选择具有无图标的桌面,或者选择「文件/启动器图标」来选择有图标的桌面。若选择了「已最小化应用程序的图标」,桌面就会把最小化的程序变成桌面上的图标。
126 |
127 | #### 外观 {#appearance}
128 |
129 | ##### 样式 {#appearance-styles}
130 |
131 | 
132 |
133 | 图 8. 外观样式首选项
134 | {: .caption }
135 |
136 | 这些就是 Xfce 的样式,这些样式可以控制控件(如按钮和菜单)的外观,直接在列表中选择即可切换样式。
137 |
138 | !!! tip "提示"
139 |
140 | 除了列表中已有的样式,我们可以自己下载更多更炫酷的样式。我们将在拓展阅读中提及具体操作方法。
141 |
142 | ##### 图标 {#appearance-icons}
143 |
144 | 
145 |
146 | 图 9. 外观图标首选项
147 | {: .caption }
148 |
149 | 这个列表控制图标的视觉外观,这些图标将会被显示在面板上,桌面上,文件管理器和菜单中。
150 |
151 | !!! tip "提示"
152 |
153 | 同样式一样,我们也可以自己下载安装图标。我们将在拓展阅读中提及具体操作方法。
154 |
155 | ##### 字体 {#appearance-fonts}
156 |
157 | 
158 |
159 | 图 10. 外观字体首选项
160 | {: .caption }
161 |
162 | 在「默认字体」和「默认等宽字体」中下拉菜单可以选择字体的 Family,Style 和 Size。
163 |
164 | 「启动抗锯齿」选项可消除锯齿字体,使字符具有平滑的边缘。
165 |
166 | 「提示」是一种字体渲染技术,可提高小尺寸和低屏幕分辨率时的字体质量。选择一个选项以指定如何应用提示字体。
167 |
168 | #### 窗口管理器 {#wm}
169 |
170 | xfwm4 窗口管理器也是 Xfce 桌面环境的核心模块。窗口管理器负责窗口在屏幕上的放置,提供窗口装饰,并允许它们移动,调整大小或关闭。
171 |
172 | ##### 样式 {#wm-styles}
173 |
174 | 
175 |
176 | 图 11. 窗口管理器样式首选项
177 | {: .caption }
178 |
179 | Xfce 允许用户自定义窗口的样式,「样式」对话框是用来控制窗口本身的,包括窗口的主题、标题和布局。
180 |
181 | ##### 键盘 {#wm-keyboard}
182 |
183 | 
184 |
185 | 图 12. 窗口管理器键盘首选项
186 | {: .caption }
187 |
188 | 在「键盘」对话框,我们可以双击列表中的动作选项来设置或更改快捷键。
189 |
190 | #### 面板 {#panels}
191 |
192 | Xfce-panel 也是 Xfce 的核心模块,具有应用程序启动器,面板菜单,工作区切换器等功能。
193 |
194 | 
195 |
196 | 图 13. 默认的顶部面板
197 | {: .caption }
198 |
199 | ##### 显示 {#panels-display}
200 |
201 | 
202 |
203 | 图 14. 面板显示首选项
204 | {: .caption }
205 |
206 | 面板选项的顶部可以选择要控制的面板对象。下拉选单我们可以发现,默认的「面板 2」是底部显示应用程序的 Dock。我们可以轻松得添加、删除面板(理论上可以添加无数个面板,取决与你的喜好)。
207 |
208 | 在「显示」选项卡中,我们可以修改面板的「模式」,可以使将面板设置为水平或者垂直于桌面栏。
209 |
210 | 「锁定面板」选项选中后,面板将不能被鼠标拖拽移动。
211 |
212 | 「自动隐藏」当设置为「聪明地」时,会在聚焦的窗口与面板重叠时隐藏面板。
213 |
214 | 下方的尺寸栏允许我们轻易地改变面板的尺寸。
215 |
216 | ##### 项目 {#panels-items}
217 |
218 | 
219 |
220 | 图 15. 面板项目首选项
221 | {: .caption }
222 |
223 | 项目(Items)是一项非常实用的功能,实际上就是小部件,不仅可以在面板中显示内部信息(如窗口、工作区、邮件等),还可以显示外部硬件信息(如 CPU、磁盘等)。我们可以在「项目」选项卡中管理它们。 另外我们也可以直接在对应的面板直接右键添加项目。
224 |
225 | ### 常用功能个性化 {#basic-settings}
226 |
227 | #### 文件管理器 {#file-manager}
228 |
229 | 文件管理器是 Linux 桌面环境重要的模块之一。
230 |
231 | Thunar 是 Xfce 桌面环境的现代文件管理器。Thunar 从一开始就被设计为快速且易于使用。它的用户界面干净直观,默认情况下不包含任何令人困惑或无用的选项。Thunar 可以快速启动,并且浏览文件和文件夹的过程非常快速且响应迅速。
232 |
233 | ##### 布局 {#file-manager-layout}
234 |
235 | 
236 |
237 | 图 16. 文件管理器
238 | {: .caption }
239 |
240 | 左边的侧边栏主要显示三类对象:设备、位置和网络。我们可以通过上方菜单栏的 `视图-侧边栏` 选择侧边栏显示方式是「快捷方式」或者是「树形」。通过直接右键侧边栏的空白处也可有隐藏不想显示的设备、位置或是网络中的东西。
241 |
242 | 菜单栏下方的地址栏显示目前的目录地址,可以通过菜单栏「视图」 →「位置选择器」选择「工具栏样式」或者是路径栏样式。
243 |
244 | 如果不喜欢主界面的图标显示,我们还可以选择「视图」→ 「以详细列表查看来以列表显示」。在列表显示的时候,我们可以通过「视图」→「配置栏」管理列表显示的属性。
245 |
246 | 
247 |
248 | 图 17. 文件管理器列表显示
249 | {: .caption }
250 |
251 | 还有更多可以配置的选项,可以在「编辑」→ 「首选项」中配置。
252 |
253 | 
254 |
255 | 图 18. 文件管理器首选项
256 | {: .caption }
257 |
258 | ##### 插件 {#file-manager-plugins}
259 |
260 | Thunar 提供了一套插件接口,Thunar 插件可以作为单独的软件包安装。详细按照方式将在拓展资料讲解。
261 |
262 | #### 会话管理器 {#sessions}
263 |
264 | Xfce4-session 是 Xfce 的会话管理器。它的任务是保存桌面的状态(打开的应用程序及其位置),并在下次启动时将其还原。你可以创建几个不同的会话,并在启动时选择其中一个。
265 |
266 | 在「设置管理器」的「会话和启动」中可以配置它。
267 |
268 | 
269 |
270 | 图 19. 会话与启动
271 | {: .caption }
272 |
273 | ##### 应用程序自启动 {#autostart}
274 |
275 | 
276 |
277 | 图 20. 自启动首选项
278 | {: .caption }
279 |
280 | 在这个列表中我们可以轻松管理自启动执行的程序。
281 |
282 | ## 命令行操作 {#shell}
283 |
284 | ### 为什么要用命令行 {#why-shell}
285 |
286 | 图形界面非常方便,为什么 Linux 的用户还热衷于命令行的使用呢?
287 |
288 | #### 效率 {#shell-efficiency}
289 |
290 | 使用命令行操作可以减少鼠标操作,我们经常可以使用一条命令来代替好几次的鼠标单击。例如如果我们想要移动某一个文件,我们要执行下面步骤:
291 |
292 | - 打开文件所在的文件夹 `/path/to/source/`
293 | - 打开目标文件夹 `/path/to/dest/`
294 | - 从 `source` 文件夹拖拽文件 `file.txt` 到 `dest` 文件夹中
295 |
296 | 然而使用命令行,我们只需要执行一条指令。
297 |
298 | ```console
299 | $ mv /path/to/source/file.txt /path/to/dest/
300 | ```
301 |
302 | 可能在初学者看来,熟记这条指令并不容易,但是从长远上看,熟悉了命令行之后再加上有自动补全的 shell 程序,使用命令行可以节省大量时间。
303 |
304 | !!! tip "路径的概念"
305 |
306 | 对于不太熟悉命令行的用户来说,路径的概念可能会在最开始带来一些困惑。这里做一些简单的介绍。
307 |
308 | 在 Windows 系统下,路径是以反斜杠 `\` 分隔的(Windows 系统也可以使用 `/` 分隔路径),例如:
309 |
310 | ```
311 | C:\Windows\explorer.exe
312 | ```
313 |
314 | 代表 C 盘下 Windows 目录下的 `explorer.exe` 文件。而在类 UNIX 系统中,路径是以正斜杠 `/` 分隔的,例如:
315 |
316 | ```
317 | /bin/ls
318 | ```
319 |
320 | 代表根目录下的 `bin` 文件夹下的 `ls` 文件。可以发现除了分隔符不同以外,类 UNIX 系统也不采用 Windows 下的盘符机制,而是采用统一的根目录。Linux 的文件系统层次结构会在[第五章](../Ch05/index.md#fhs)介绍。
321 |
322 | 另外,以上的路径都是绝对路径,还有一种「相对路径」:
323 |
324 | ```shell
325 | file1.txt # 当前目录下的 file1.txt 文件
326 | ./file1.txt # 当前目录下的 file1.txt 文件
327 | ./file2.txt # 当前目录下的 file2.txt 文件
328 | ../file3.txt # 上一级目录(父目录)下的 file3.txt 文件
329 | ../abc/file4.txt # 上一级目录(父目录)下的 abc 文件夹下的 file4.txt 文件
330 | ../../file5.txt # 上上级目录下的 file5.txt
331 | ```
332 |
333 | 它们的关系是这样的:
334 |
335 | ```plain
336 | 上上级目录/
337 | |___ file5.txt
338 | |___ 上一级目录/
339 | |___ file3.txt
340 | |___ 当前目录/
341 | | |___ file1.txt
342 | | |___ file2.txt
343 | |___ abc/
344 | |___ file4.txt
345 | ```
346 |
347 | 每个正在运行中的进程(包括 Shell)都有自己的「当前工作目录」(当前所在的目录),进程可以切换自己的当前工作目录,以上的相对路径都是相对于当前工作目录的。可以发现,不管当前工作目录在哪里,绝对路径对应的文件都是一致的,而相对路径对应的文件就会随着当前工作目录的变化而变化。
348 |
349 | 特别地,用户的主目录(一般是 `/home/<用户名>`)可以被简写为 `~`。例如,`~/work/test.c` 可能是 `/home/ustc/work/test.c` 的缩写。
350 |
351 | #### 自动化脚本 {#shell-automation}
352 |
353 | !!! tip "提示"
354 |
355 | Shell 脚本的使用将在[第六章](../Ch06/index.md)详细讲解。
356 |
357 | Shell 脚本可以帮助程序员自动执行重复的任务。例如我们想自动编译运行一个 C 语言程序 `main.c`,我们可以在该文件的目录新建一个脚本 `run.sh`.
358 |
359 | 两个文件内容分别如下。
360 |
361 | ```c
362 | // main.c
363 |
364 | #include
365 | int main() {
366 | printf("Hello world!\n");
367 | return 0;
368 | }
369 | ```
370 |
371 | ```shell
372 | # run.sh
373 |
374 | gcc main.c -o main.out
375 | ./main.out
376 | rm main.out
377 | ```
378 |
379 | 和其他地方不一样,在 Shell 中运行程序时,程序名(`main.out`)前面必须有 `./`。这是因为因为我们的工作目录不包含在环境变量(`$PATH`)中,故如果不加 `./` 则系统会找不到程序。系统中安装的程序(例如 `gcc`)一般会放在 `$PATH` 环境变量中包含了的路径下,故运行它们不需要使用 `./`。
380 |
381 | !!! tip "为何需要加上 `./`"
382 |
383 | 一个简单的原因是,如果像 Windows 那样直接输入可执行文件的名称就能运行程序的话,攻击者可能将恶意代码写入到用户工作目录下以一些常见的命令(例如 `ls` 和 `cat`)为名称的文件中,并加入执行(`x`)权限。不知情的用户执行这些命令就会导致恶意代码的运行。而 Linux 系统下不加 `./` 运行的是 `PATH` 环境变量中列出的目录下的可执行文件,这些位置一般只有 `root` 用户能写入,避免了上面的问题。此外另一个原因是,这还避免了用户的程序和系统中安装的程序因为重名而冲突。
384 |
385 | 之后我们直接输入
386 |
387 | ```console
388 | $ sh run.sh
389 | ```
390 |
391 | 即可看到程序输出结果。
392 |
393 | !!! tip "使用 `make`"
394 |
395 | 就这个例子而言,对于更加复杂的项目来说,编写 Makefile 并使用 `make` 构建是一个更好的主意。相关内容在[第七章](../Ch07/index.md#c-build-tools)进行了介绍。
396 |
397 | #### 节省资源 {#shell-resource-conservation}
398 |
399 | 图形界面对资源的消耗是不可忽略的,在绝大部分的服务器中都没有使用图形界面,节约资源。
400 |
401 | #### 进行高级的系统维护工作 {#shell-system-maintenance}
402 |
403 | 一些高级的系统维护任务只能通过命令行来完成,或者需要手工编写复杂的配置文件,因为相关的程序并没有提供图形界面的控制面板。
404 |
405 | #### 使用命令行看上去很酷 {#shell-duang}
406 |
407 | 影视作品中,操作命令行的总是技术高超的黑客。现在轮到你来操作命令行了,难道不是很酷的一件事情吗?
408 |
409 | !!! tip "`cmatrix`:命令行界面模拟《黑客帝国》特效显示"
410 |
411 | Linux 的命令行界面有一些显示效果很有意思的程序,`cmatrix` 是其中之一。
412 |
413 | 使用以下命令安装(软件的安装将在[第三章](../Ch03/index.md)详细介绍):
414 |
415 | ```console
416 | $ sudo apt install cmatrix
417 | ```
418 |
419 | 之后就可以使用 `cmatrix` 命令来查看特效效果了。按下 `q` 键或者 Ctrl + C 组合键退出。
420 |
421 | ### 什么是 shell {#what-is-shell}
422 |
423 | 上面所说的命令行,实际上指的就是 shell。shell 是 Linux 中的一类程序,它可以接受通过键盘输入的命令,然后把命令交给系统执行,并将命令的输出返回给用户。现在几乎所有的 Linux 发行版都提供了一个叫 Bash 的 shell 程序,是传统 shell 的“增强版”。
424 |
425 | ??? note "一些关于 shell 的细节知识"
426 |
427 | 但凡使用 Linux,必然要与之交互。广义上讲,能与用户交互的程序都符合 shell 的定义(比如图形界面可以识别鼠标位置信息,点击操作和键盘快捷键)。然而 Linux 本身以命令行工具为主,而 shell 狭义上就是命令行解释工具,即允许用户在一定程度上用 shell 的语言来调用程序。
428 |
429 | Shell 是非常重要的程序。如果发行版中没有 shell,用户便无法控制计算机了。
430 |
431 | ### 如何打开 shell {#how-to-use-shell}
432 |
433 | 在图形界面中,我们需要另一个和 shell 交互的程序,叫做终端模拟器,通常它的名称为「终端(Terminal)」。我们可以在「所有程序」找到它。
434 |
435 | 
436 |
437 | 图 21. 终端在菜单的位置
438 | {: .caption }
439 |
440 | 打开后如下图。
441 |
442 | 
443 |
444 | 图 22. 终端界面
445 | {: .caption }
446 |
447 | ### 几条简单的命令 {#shell-commands}
448 |
449 | - `ls`: 列出(**l**i**s**t)目录的内容
450 | - `cd`: 更改目录(**c**hange **d**irectory)
451 | - `pwd`: 查看当前所在的目录(**p**rint **w**orking **d**irectory)
452 |
453 | 更多的命令行操作我们将在[第三章](../Ch03/index.md)和[第六章](../Ch06/index.md)详细介绍。
454 |
455 | #### 示例 1 {#shell-commands-example-1}
456 |
457 | ```console
458 | $ pwd
459 | ```
460 |
461 | 会打印出当前所在的目录:
462 |
463 | ```text
464 | /home/ustc
465 | ```
466 |
467 | #### 示例 2 {#shell-commands-example-2}
468 |
469 | ```console
470 | $ ls
471 | ```
472 |
473 | 会打印出当前目录的内容:
474 |
475 | ```text
476 | Desktop Documents Music Pictures Public Templates Videos
477 | ```
478 |
479 | #### 示例 3 {#shell-commands-example-3}
480 |
481 | ```console
482 | $ cd Desktop # 这里的 Desktop 是相对路径,指的就是当前目录下的 Desktop 文件夹
483 | $ ls
484 | ```
485 |
486 | 进入桌面,并打印出桌面的内容。
487 |
488 | ```text
489 | hi.txt
490 | ```
491 |
492 | !!! info "注意"
493 |
494 | 显示内容与计算机文件状态有关,并不是每个人都会显示相同的内容。
495 |
496 | ## 搭建简易的网站 {#website}
497 |
498 | 搭建网站在 Linux 环境中较 Windows 中更加容易,仅需一两行命令,即可搭建成型的网站。
499 |
500 | ### WordPress
501 |
502 | WordPress 是一个以 PHP 和 MySQL 为平台的自由开源的博客软件和内容管理系统。
503 |
504 | 由于 WordPress 是一个动态的博客软件,它需要涉及到一些数据库相关的配置和 HTTP 服务器的配置,这里我们给大家准备了一个在 Ubuntu 中安装 WordPress 的[自动配置脚本](wordpress.sh)。
505 |
506 | !!! warning "安全提醒"
507 |
508 | 该自动配置脚本仅用于本讲义中展示功能用途,请谨慎在正式生产环境中使用。
509 |
510 | !!! tip "提示"
511 |
512 | 有兴趣自己配置的同学可以参阅[补充材料](supplement.md)。
513 |
514 | !!! tip "虚拟机内存要求"
515 |
516 | 如果你正在使用我们提供的虚拟机,建议将虚拟机分配的内存从 1 GB 添加至至少 2 GB(2048 MB),以同时供桌面环境与 MySQL 数据库流畅使用。
517 |
518 | 下载脚本要使用 `curl` 命令,我们要先安装 curl。
519 |
520 | ```console
521 | $ sudo apt install curl
522 | ```
523 |
524 | !!! note "关于记号约定"
525 |
526 | 如果直接复制以上的命令到终端,你可能会看到以下场景:
527 |
528 | ```shell
529 | ustc@ustclug-linux101:~$ $ sudo apt install curl
530 | $:未找到命令
531 | ```
532 |
533 | 这里给出的命令开头的 `$` 代表该命令以非 root(最高权限)执行,并非命令的一部分。打开终端后,你也可以看到在光标前面有一个 `$` 符号。所以如果需要复制命令,请勿复制最开头的 `$`。
534 |
535 | 可以阅读[记号约定](../notations.md)了解有关信息。
536 |
537 | 打开终端并运行:
538 |
539 | ```console
540 | $ curl -fsSL https://101.lug.ustc.edu.cn/Ch02/wordpress.sh > wordpress.sh
541 | $ # 可以阅读 wordpress.sh 了解其运行的命令,检查代码无误后执行:
542 | $ sudo bash wordpress.sh
543 | ```
544 |
545 | 等待片刻即可完成安装。
546 |
547 | !!! warning "注意"
548 |
549 | 这个脚本随机生成了 WordPress 数据库的密码并储存在了 `/root` 目录下。
550 |
551 | !!! warning "避免执行类似于 curl | sh 的命令"
552 |
553 | 在本节之前的版本中,以上的命令是:
554 |
555 | ```console
556 | $ curl -fsSL https://101.lug.ustc.edu.cn/Ch02/wordpress.sh | sudo bash
557 | ```
558 |
559 | 它的功能是从对应的 URL 获取脚本内容后,直接用 `sudo bash` 去执行。但是从安全性的角度这是不恰当的,因为网络上的脚本有可能包含恶意内容,直接执行可能会对系统带来安全风险。因此相关命令修改为了首先下载脚本到文件中(`wordpress.sh` 文件),然后再执行。如果对脚本内容有疑虑,则可以阅读脚本内容,检查其是否和你的预期相一致。
560 |
561 | 最后我们打开浏览器并进入 `http://localhost/blog` 来完成最后的配置。
562 |
563 | 
564 |
565 | 图 23. WordPress 的初始化配置界面
566 | {: .caption }
567 |
568 | ### Jekyll
569 |
570 | Jekyll 是一个将纯文本转化为静态博客和网站的工具。
571 |
572 | 我们只需要通过命令行安装它。
573 |
574 | ```console
575 | $ sudo apt install jekyll
576 | ```
577 |
578 | 再输入几行命令用于创建网站:
579 |
580 | ```console
581 | $ jekyll new my-awesome-site
582 | $ cd my-awesome-site
583 | $ jekyll serve
584 | ```
585 |
586 | 打开浏览器,在浏览器中输入 `localhost:4000` 进入我们搭建的网站。
587 |
588 | {: .img-border }
589 |
590 | 图 24. Jekyll 的默认网页
591 | {: .caption }
592 |
593 | ## 思考题 {#questions}
594 |
595 | !!! question "桌面环境的选择"
596 |
597 | Linux 存在着各式各样的桌面环境,在你选择它们的时候,你会关注哪些方面呢?
598 |
599 | !!! question "桌面环境的使用场合"
600 |
601 | 如果你见过 Linux 服务器用的发行版,你会发现它们绝大多数都没有默认安装桌面环境,这是什么原因呢?
602 |
603 | ## 引用来源 {#references .no-underline}
604 |
605 | - [Xfce 文档](https://docs.xfce.org/xfce/start)
606 | - [Ubuntu 下安装 WordPress](https://ubuntu.com/tutorials/install-and-configure-wordpress)
607 |
--------------------------------------------------------------------------------
/docs/Ch02/solution.md:
--------------------------------------------------------------------------------
1 | ---
2 | icon: material/tooltip-question
3 | ---
4 |
5 | # 思考题解答 {#solution}
6 |
7 | ## 桌面环境的选择 {#choice-of-desktop-environment}
8 |
9 | !!! tip "提示"
10 |
11 | 这是一个相对主观的问题,不同的人可能会给出完全不同的回答。可以尝试一下其他的桌面环境之后再想一想。
12 |
13 | ??? info "解答"
14 |
15 | 在选择桌面环境的时候,用户可能会从以下几个方面去考虑:
16 |
17 | - 桌面环境需要多少系统资源?我们的虚拟机镜像选择 XUbuntu 的一个重要原因就是我们希望镜像能够在配置较低的电脑上也能流畅运行,而 Xfce 桌面环境是非常轻量级的。有些桌面环境,如 KDE 或者 GNOME,需要比较多的系统资源,在老旧的电脑上会非常卡顿。而与 Xfce 类似的轻量桌面环境还有 LXDE 等。
18 | - 桌面环境是否美观?当然不同的人对「美观」的要求也是不同的。在十余年前,曾经流行过通过 Compiz 为桌面环境添加绚丽的 3D 效果。如今,KDE 和 GNOME 也为用户提供了美观的界面。另外还有一些桌面环境专注于美观的用户界面,例如 Enlightenment (e17)。
19 | - 桌面环境是否可以充分自定义?不同的用户对桌面环境的要求不同,并且也会有不同的设置。例如,GNOME 通过拓展程序可以实现丰富的功能。
20 | - 桌面环境是否高效?追求效率的一个例子是,一部分用户会选择「平铺式窗口管理器」(Tiling window manager) 来管理窗口。在平铺式中,各个窗口不重叠,所有窗口合起来占满了整个屏幕。典型的平铺式窗口管理器有 i3, awesome 等。
21 |
22 | ## 桌面环境的使用场合 {#when-to-use-desktop}
23 |
24 | ??? info "解答"
25 |
26 | 在 Linux 服务器的环境下,桌面环境不是必需品。我们知道,Linux 下桌面环境也只是一个(可选的)软件(Windows Server 中,桌面环境也不是必须安装的)。服务器中的配置完全可以在命令行中完成。
27 |
28 | 并且,安装桌面环境会占用额外的资源,尤其对于性能较低的服务器(例如在各种云服务器厂商上可以购买到的配置最低的机器,或者一些 SoC 嵌入式设备)。同时配置远程连接桌面(如使用 VNC)的过程也是比较麻烦的,且桌面环境也并没有命令行环境简单可靠。
29 |
--------------------------------------------------------------------------------
/docs/Ch02/supplement.md:
--------------------------------------------------------------------------------
1 | ---
2 | icon: material/puzzle
3 | ---
4 |
5 | # 拓展阅读 {#supplement}
6 |
7 | !!! Failure "本文目前尚未完稿,存在诸多未尽章节且未经审阅,不是正式版本。"
8 |
9 | ## 桌面环境的安装 {#desktop-install}
10 |
11 | 如果你安装的系统没有预装桌面环境,或者你想要更换其他桌面环境,我们就需要自己安装桌面环境。
12 |
13 | 下面就来介绍如何手动安装桌面环境。
14 |
15 | 以在 Ubuntu Server 18.04.3 (未包含图形环境的系统) 上安装桌面环境为例。只需要执行以下步骤:
16 |
17 | 安装 `ubuntu-gnome-desktop` 软件:
18 |
19 | ```console
20 | $ sudo apt install ubuntu-gnome-desktop
21 | ```
22 |
23 | 接下来的提示中按输入 Y 回车即安装。
24 |
25 | !!! tip "注意"
26 |
27 | 若安装非常缓慢,可以尝试更换国内的软件源。[科大源更换教程](https://mirrors.ustc.edu.cn/help/ubuntu.html)
28 |
29 | 安装完成后输入:
30 |
31 | ```console
32 | $ sudo reboot
33 | ```
34 |
35 | 重启后可以看到,GNOME 桌面已经安装完成。我们拥有了图形界面。
36 |
37 | 
38 |
39 | 可以看到,通过简单几步,我们的桌面环境就安装成功了。
40 |
41 | ??? example "安装 Plasma 桌面"
42 |
43 | 同理,如果你喜欢 KDE plasma 桌面,只需要执行
44 |
45 | ```console
46 | $ sudo apt install kde-plasma-desktop
47 | $ sudo reboot
48 | ```
49 |
50 | ## GNOME 相关
51 |
52 | ### GNOME 桌面环境的个性化 {#gnome-personalization}
53 |
54 | 大部分桌面环境都支持主题的个性化。例如:窗口样式,按钮样式,Dock 样式,指针样式等等。
55 |
56 | 在 GNOME 桌面下用户可以轻松更换主题。
57 | 首先安装 `gnome-tweaks` 软件:
58 |
59 | ```console
60 | $ sudo apt install gnome-tweaks
61 | ```
62 |
63 | 在 [GNOME Look](https://www.gnome-look.org/) 中找到自己喜欢的主题。
64 |
65 | !!! info "注意"
66 |
67 | 不同类型的主题有不同的安装方法,一般在主题介绍页面有显示。
68 |
69 | 点击 Download 下载一个压缩包:
70 |
71 | 
72 |
73 | 解压后放到 `~/.themes` 文件夹,若不存在该文件夹则创建一个。
74 |
75 | ```console
76 | $ mkdir ~/.themes
77 | ```
78 |
79 | !!! tip "家目录与隐藏文件"
80 |
81 | `~` 是用来简单表示用户的家目录(主目录)的符号,普通用户的家目录一般位于 `/home/用户名/`。`~/.themes` 即代表家目录下的 `.themes` 文件夹。
82 |
83 | 以 `.` 开头的文件和文件夹是隐藏的。如果使用图形界面解压缩,有可能看不到 `.themes` 目录。此时可以按下 Ctrl + H 快捷键显示隐藏文件夹。
84 |
85 | 输入:
86 |
87 | ```console
88 | $ gnome-tweaks
89 | ```
90 |
91 | 打开 gnome-tweaks,在外观选项中选中想要的主题。
92 |
93 | 重启 GNOME 即可完成主题更换。
94 |
95 | !!! tip "提示"
96 |
97 | 你也可以使用 ocs-url 软件,在网页中直接安装主题,参考本章附录
98 |
99 | 如果你觉得商店中的主题不符合你的审美,你也可以自己制作一款独一无二的主题,或者直接修改现有的主题。
100 |
101 | #### GNOME 的 Shell 扩展 {#gnome-extensions}
102 |
103 | GNOME 支持很多扩展,并且有一个专门用于扩展的网站。https://extensions.gnome.org/
104 |
105 | 要使用 GNOME 扩展,我们要先安装 `gnome-shell-extensions`。
106 |
107 | ```console
108 | $ sudo apt install gnome-shell-extensions
109 | ```
110 |
111 | 接下来进入扩展插件的网站并选择其中一款扩展:
112 |
113 | Caffeine: 允许用户停用系统屏幕保护和自动休眠。
114 |
115 | 
116 |
117 | 先来查看我们正在使用的 GNOME 版本:
118 |
119 | ```console
120 | $ gnome-shell --version
121 | ```
122 |
123 | 在插件网页中下载对应版本的压缩包并解压到一个文件夹。
124 |
125 | 这时我们打开文件夹里的 `metadata.json` 文件。
126 |
127 | 然后将文件夹的名字改为 `metadata.json` 中的 UUID:
128 |
129 | 
130 |
131 | 本例中,UUID 为 "caffeine@patapon.info"
132 |
133 | 
134 |
135 | 并将该文件夹放到 `~/.local/share/gnome-shell/extensions/` 中。
136 |
137 | 打开 `gnome-tweaks`。
138 |
139 | ```console
140 | $ gnome-tweaks
141 | ```
142 |
143 | 在扩展一栏即可启用我们刚刚装的 caffeine。
144 |
145 | 
146 |
147 | !!! tip "提示"
148 |
149 | 手动安装显然过于复杂,我们完全可以只使用浏览器来完成扩展插件的管理和安装。
150 |
151 | 首先安装浏览器插件:
152 |
153 | 对于 Google Chrome、Chromium 和 Vivaldi:[Chrome Web 商店](https://chrome.google.com/webstore/detail/gnome-shell-integration/gphhapmejobijbbhgpjhcjognlahblep)
154 |
155 | 对于 Firefox: [Mozilla Addons](https://addons.mozilla.org/en-US/firefox/addon/gnome-shell-integration/)
156 |
157 | 再安装本地连接器:
158 |
159 | ```console
160 | $ sudo apt install chrome-gnome-shell
161 | ```
162 |
163 | 即可在 https://extensions.gnome.org/ 网页中管理、安装插件。
164 |
165 | 
166 |
167 | ## Xfce 联网下载安装更多主题 {#xfce-themes}
168 |
169 | 除了系统自带的外观样式和图标外,网络上有更多的主题提供下载。例如在 [Xfce-look](https://www.xfce-look.org/) 上,就有上万个不同类型的主题。安装方法也十分简单。
170 |
171 | 
172 |
173 | 我们可以选中任意一款主题。我们以下面这个为例。
174 |
175 | 
176 |
177 | 点击 Download 按钮下载它,一般会得到压缩包格式的文件。我们打开设置管理器中的「外观」首选项,把下载好的压缩包文件直接拖拽到样式列表中。
178 |
179 | 
180 |
181 | 选中刚刚拖拽进去的主题即可更换。
182 |
183 | 
184 |
185 | !!! tip "提示"
186 |
187 | 有些主题包是很多样式(包括窗口、图标和外观样式)成套出现的,如果安装的主题包括了 Xfwm4 窗口管理器主题的话,我们会发现在「窗口管理器」首选项中,该主题也会出现:
188 |
189 | 
190 |
191 | 选中后,窗口样式就变了。
192 |
193 | 更换主题包后,是不是整个系统变得高大上了起来?
194 |
195 | !!! tip "提示"
196 |
197 | 我们可以安装插件在网页上直接安装主题。
198 |
199 | 在 中,下载 ocs-url,下载时选择 deb 后缀的安装包。
200 |
201 | 
202 |
203 | 命令行进入下载好的安装包所在文件夹,输入以下命令。注意替换「下载的包」为你的安装包名,例如如果下载的安装包名为 `ocs-url_3.1.0-0ubuntu1_amd64.deb`,则 `install` 后面的参数为 `./ocs-url_3.1.0-0ubuntu1_amd64.deb`。
204 |
205 | ```console
206 | $ sudo apt install ./下载的包.deb
207 | ```
208 |
209 | 即可完成安装。
210 |
211 | 接下来在 中所有的主题只需要点击 Install 即可自动安装到相应的目录。你只需要在 gnome-tweaks 中更换主题即可。
212 |
213 | ## 终端的个性化 {#terminal-personalization}
214 |
215 | 使用 Linux 系统时,不可避免接触终端命令行操作,但是默认的终端黑底白字。有什么办法可以既美化终端,又提高工作效率呢?下面我们介绍一些美化终端的方法。
216 |
217 | ### 更换 Shell {#chsh}
218 |
219 | 在此之前我们可以通过:
220 |
221 | ```console
222 | $ echo $SHELL
223 | ```
224 |
225 | 检查目前我们正在用的是什么 Shell。Ubuntu 默认使用 Bash,在这里推荐一个更加强大的 Shell 工具——Z shell(Zsh)。
226 |
227 | #### Zsh
228 |
229 | 首先通过 apt 安装 `zsh`:
230 |
231 | ```console
232 | $ sudo apt install zsh
233 | ```
234 |
235 | 将 zsh 设定为默认 shell:
236 |
237 | ```console
238 | $ chsh -s /bin/zsh
239 | ```
240 |
241 | 重启后打开终端就会发现 shell 已经变成了 zsh。
242 |
243 | 第一次打开 zsh 会有首次使用提示,这里我们按 0 跳过。
244 |
245 | 接下来的提示中按 Y 回车即安装。
246 |
247 | 然而这时的 zsh 仍然是黑底白字,要让它变好看,我们需要对 zsh 进行配置。
248 |
249 | ##### oh-my-zsh
250 |
251 | oh-my-zsh 是一个管理 zsh 配置的框架,评价也非常好。
252 |
253 | ```console
254 | $ sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
255 | ```
256 |
257 | !!! info "如果无法下载安装脚本"
258 |
259 | 如果遇到了网络连通性问题,可以使用以下替换命令:
260 |
261 | ```console
262 | $ REMOTE=https://mirrors.tuna.tsinghua.edu.cn/git/ohmyzsh.git sh -c "$(curl -fsSL https://mirrors.ustc.edu.cn/misc/ohmyzsh-install.sh)"
263 | ```
264 |
265 | 安装完成后就可以看到 shell 不再是黑底白字,让人感到焕然一新,然而也许这样并不能让你满足。
266 |
267 | 我们可以修改 `~/.zshrc` 里的这一行中的引号部分切换主题:
268 |
269 | ZSH_THEME="robbyrussell"
270 |
271 | 如果只想试用某个主题,可以使用以下命令:
272 |
273 | omz theme use robbyrussell
274 |
275 | 来临时试用某主题。
276 | 具体主题可以在 [oh-my-zsh 的项目 Wiki](https://github.com/ohmyzsh/ohmyzsh/wiki/Themes) 中找到。
277 | 当然你也可以尝试自己做一个主题。
278 |
279 | ## 其它的个性化 {#other-personalizations}
280 |
281 | 上面内容都是外观上的个性化,更多地,Linux 系统的可客制化还体现在一些配置文件上。
282 |
283 | ### etc 目录
284 |
285 | `/etc` 目录是包含几乎所有 Linux 系统配置的一个文件夹。
286 |
287 | !!! info "tips"
288 |
289 | etc 是 "et cetera" 的简称,意思是 "and so on",在 Unix 初期人们实现 `etc` 文件夹就是为了保留配置文件,数据文件,套接字文件或其他文件用的。随着时间流逝,文件夹的含义已经更改,但是名字 etc 没有更改。现在 `/etc` 目录是所有配置文件的集中地,可以看作 Linux 系统的神经中枢。
290 |
291 | 下面介绍几个常用的配置文件:
292 |
293 | - `/etc/fstab` 系统磁盘挂载相关配置;
294 | - `/etc/bash.bashrc` 启动 Bash 时读取的配置脚本;
295 | - `/etc/sudoers` sudo 权限的配置;
296 | - `/etc/hosts` 主机名与 IP 映射关系的配置。
297 |
298 | ??? example "示例"
299 |
300 | 当我们登录用户成功时:
301 |
302 | ```console
303 | $ sudo login
304 | ```
305 |
306 | 会提示以下信息:
307 |
308 | ```text
309 | Welcome to Ubuntu 18.04.3 LTS (GNU/Linux 5.3.0-28-generic x86_64)
310 | * Documentation: https://help.ubuntu.com
311 | * Management: https://landscape.canonical.com
312 | * Support: https://ubuntu.com/advantage
313 | * Canonical Livepatch is available for installation.
314 | - Reduce system reboots and improve kernel security. Activate at:
315 | https://ubuntu.com/livepatch
316 | 125 个可升级软件包。
317 | 0 个安全更新。
318 | Your Hardware Enablement Stack (HWE) is supported until April 2023.
319 | *** 需要重启系统 ***
320 | The programs included with the Ubuntu system are free software;
321 | the exact distribution terms for each program are described in the
322 | individual files in /usr/share/doc/*/copyright.
323 |
324 | Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
325 | applicable law.
326 | ```
327 |
328 | Ubuntu 下这些提示信息都可以在 `/etc/update-motd.d/` 目录下修改, 登录后,将会在该目录依数字递增顺序执行该目录下的脚本。
329 |
330 | !!! info "提示"
331 |
332 | 有的 Linux 发行版的 MOTD (Message Of The Day) 配置在 `/etc/motd`
333 |
334 | 我们在 `/etc/update-motd.d/` 目录下新建一个文件 `99-test`,写入下面内容:
335 |
336 | ```shell
337 | #!/bin/sh
338 | echo helloworld
339 | ```
340 |
341 | 然后:
342 |
343 | ```console
344 | $ sudo chmod +x /etc/update-motd.d/99-test
345 | ```
346 |
347 | 设置好权限,登录后就可以看到在末尾加上了我们在 `99-test` 文件中 echo 的内容。
348 |
349 | 当然如果你不希望显示上面的更新提示内容,也可以直接找到对应的文件删除或修改。
350 |
351 | ## 搭建简易的网站 {#website}
352 |
353 | ### WordPress 的手动配置
354 |
355 | ```console
356 | $ sudo apt install -y wordpress php libapache2-mod-php mysql-server php-mysql
357 | ```
358 |
359 | 这样就已经把 WordPress 所依赖的环境搭建好了,我们只需要稍微配置一下它。
360 |
361 | 创建一个 `/etc/apache2/sites-available/wordpress.conf` 文件,填入下面内容:
362 |
363 | ```apache
364 | Alias /blog /usr/share/wordpress
365 |
366 | Options FollowSymLinks
367 | AllowOverride Limit Options FileInfo
368 | DirectoryIndex index.php
369 | Order allow,deny
370 | Allow from all
371 |
372 |
373 | Options FollowSymLinks
374 | Order allow,deny
375 | Allow from all
376 |
377 | ```
378 |
379 | 保存后输入命令来重启 `apache2`:
380 |
381 | ```console
382 | $ sudo a2ensite wordpress
383 | $ sudo a2enmod rewrite
384 | $ sudo service apache2 reload
385 | ```
386 |
387 | 再配置数据库相关内容:
388 |
389 | ```console
390 | $ sudo mysql -u root
391 | ```
392 |
393 | 出现以下信息时:
394 |
395 | ```text
396 | Welcome to the MySQL monitor. Commands end with ; or \g.
397 | Your MySQL connection id is 2
398 | Server version: 5.7.29-0ubuntu0.18.04.1 (Ubuntu)
399 | Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.
400 | Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners.
401 | Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
402 | ```
403 |
404 | Ubuntu 18.04 默认安装的是 MySQL 5.7。参照下面的命令,输入,其中 `` 替换为你自己设定的密码:
405 |
406 | ```mysql
407 | mysql> CREATE DATABASE wordpress;
408 | mysql> GRANT SELECT,INSERT,UPDATE,DELETE,CREATE,DROP,ALTER
409 | -> ON wordpress.*
410 | -> TO wordpress@localhost
411 | -> IDENTIFIED BY '';
412 | mysql> FLUSH PRIVILEGES;
413 | ```
414 |
415 | Ubuntu 20.04 默认安装的是 MySQL 8.0。由于其不再支持使用 `GRANT` 直接创建用户,命令需要小幅修改:
416 |
417 | ```mysql
418 | mysql> CREATE DATABASE wordpress;
419 | mysql> CREATE USER wordpress@localhost IDENTIFIED BY '';
420 | mysql> GRANT SELECT,INSERT,UPDATE,DELETE,CREATE,DROP,ALTER
421 | -> ON wordpress.*
422 | -> TO wordpress@localhost;
423 | mysql> FLUSH PRIVILEGES;
424 | ```
425 |
426 | 这里每次执行成功都会得到:
427 |
428 | ```text
429 | Query OK, 1 row affected (0,00 sec)
430 | ```
431 |
432 | 退出 MySQL 命令行:
433 |
434 | ```mysql
435 | mysql> quit
436 | ```
437 |
438 | 编辑我们的 WordPress 配置 `/etc/wordpress/config-localhost.php`
439 |
440 | 写入以下内容,其中 `` 为刚才设定的数据库密码。
441 |
442 | ```php
443 | ');
447 | define('DB_HOST', 'localhost');
448 | define('DB_COLLATE', 'utf8mb4_general_ci');
449 | define('WP_CONTENT_DIR', '/usr/share/wordpress/wp-content');
450 | ?>
451 | ```
452 |
453 | 然后输入:
454 |
455 | ```console
456 | $ sudo service mysql start
457 | ```
458 |
459 | 启动数据库。
460 |
461 | 最后我们打开浏览器并进入 `localhost/blog` 来完成最后的配置。
462 |
463 | ## 引用来源 {#references .no-underline}
464 |
465 | - [How to install themes with GNOME tweak tool? - Ask Ubuntu](https://askubuntu.com/a/1128098/612877)
466 |
--------------------------------------------------------------------------------
/docs/Ch02/wordpress.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -eu
4 |
5 | # Is user root?
6 | if [ "$(id -u)" != "0" ]; then
7 | echo "本脚本需以 root 身份运行"
8 | echo "在大多数情况下,可以在你执行脚本的命令前加上 sudo 来以 root 身份运行"
9 | exit 1
10 | fi
11 |
12 | PASSWORD_FILE="/root/mysql_wordpress_password.txt"
13 |
14 | echo "安装有关依赖:Wordpress, PHP, MySQL 与 Apache"
15 | export DEBIAN_FRONTEND=noninteractive
16 | apt-get update
17 | apt-get install -y wordpress php libapache2-mod-php mysql-server php-mysql
18 |
19 | echo "在 Apache 中添加 Wordpress 站点"
20 | cat > /etc/apache2/sites-available/wordpress.conf << EOF
21 | Alias /blog /usr/share/wordpress
22 |
23 | Options FollowSymLinks
24 | AllowOverride Limit Options FileInfo
25 | DirectoryIndex index.php
26 | Order allow,deny
27 | Allow from all
28 |
29 |
30 | Options FollowSymLinks
31 | Order allow,deny
32 | Allow from all
33 |
34 | EOF
35 |
36 | echo "在 Apache 中启用 Wordpress 站点与 rewrite 模块,并启动 apache2"
37 | a2ensite wordpress
38 | a2enmod rewrite
39 | service apache2 restart
40 |
41 | echo "生成随机数据库密码,并保存至 $PASSWORD_FILE"
42 | DB_PASSWORD=$(tr -dc a-z0-9A-Z < /dev/urandom | head -c 8)
43 | echo "${DB_PASSWORD}" > "$PASSWORD_FILE"
44 |
45 | echo "启动并创建 MySQL 数据库 wordpress"
46 | service mysql start
47 | mysql -u root -e "CREATE DATABASE IF NOT EXISTS wordpress DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci;"
48 |
49 | echo "配置 MySQL 用户 wordpress 相关权限"
50 | mysql -u root -e "CREATE USER IF NOT EXISTS wordpress@localhost IDENTIFIED BY '${DB_PASSWORD}';"
51 | mysql -u root -e "GRANT SELECT,INSERT,UPDATE,DELETE,CREATE,DROP,ALTER ON wordpress.* TO wordpress@localhost;"
52 | mysql -u root -e "FLUSH PRIVILEGES;"
53 |
54 | echo "配置 Wordpress"
55 | cat > /etc/wordpress/config-localhost.php << EOF
56 |
64 | EOF
65 |
66 | echo "已完成。使用浏览器打开 http://localhost/blog 以完成最后的配置"
67 |
--------------------------------------------------------------------------------
/docs/Ch03/images/link.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch03/images/link.png
--------------------------------------------------------------------------------
/docs/Ch03/images/vscode-ubuntu-application-store.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch03/images/vscode-ubuntu-application-store.png
--------------------------------------------------------------------------------
/docs/Ch03/solution.md:
--------------------------------------------------------------------------------
1 | ---
2 | icon: material/tooltip-question
3 | ---
4 |
5 | # 思考题解答 {#solution}
6 |
7 | ## Матрёшка {#meta}
8 |
9 | !!! tip "提示"
10 |
11 | `man` 和 `tldr` 的用途是?
12 |
13 | ??? info "解答"
14 |
15 | 答案非常简单:`man man` / `man tldr`, 和 `tldr man` / `tldr tldr` 即可。
16 |
17 | ## 查找需要安装的软件包 {#finding-package-you-need}
18 |
19 | !!! tip "提示"
20 |
21 | `apt` 有一些相关的小工具,可能可以帮到你。
22 |
23 | ??? info "解答"
24 |
25 | 我们可以使用 `apt-file`(需要手动安装)来查找包含某个特定文件的软件包。在运行 `sudo apt-file update` 建立索引之后,可以用 `apt-file search 文件名` 来进行搜索。
26 |
27 | 一个例子:
28 |
29 | ```console
30 | $ apt-file search libc++.so
31 | libc++-7-dev: /usr/lib/llvm-7/lib/libc++.so
32 | libc++-7-dev: /usr/lib/x86_64-linux-gnu/libc++.so
33 | libc++-8-dev: /usr/lib/llvm-8/lib/libc++.so
34 | libc++-8-dev: /usr/lib/x86_64-linux-gnu/libc++.so
35 | libc++1-7: /usr/lib/llvm-7/lib/libc++.so.1
36 | libc++1-7: /usr/lib/llvm-7/lib/libc++.so.1.0
37 | libc++1-7: /usr/lib/x86_64-linux-gnu/libc++.so.1
38 | libc++1-7: /usr/lib/x86_64-linux-gnu/libc++.so.1.0
39 | libc++1-8: /usr/lib/llvm-8/lib/libc++.so.1
40 | libc++1-8: /usr/lib/llvm-8/lib/libc++.so.1.0
41 | libc++1-8: /usr/lib/x86_64-linux-gnu/libc++.so.1
42 | libc++1-8: /usr/lib/x86_64-linux-gnu/libc++.so.1.0
43 | ```
44 |
45 | ## 硬链接与软链接的判断 {#hard-and-soft-links}
46 |
47 | !!! tip "提示"
48 |
49 | 你可能需要理解 `ls -l` 输出的含义。
50 |
51 | ??? info "解答"
52 |
53 | 软链接的判断非常简单。如果是软链接的话,`ls -l` 对应的文件会明确写出其指向的文件。
54 |
55 | ```console
56 | $ ls -l /usr/bin/vim
57 | lrwxrwxrwx 1 root root 21 Jul 21 2019 /usr/bin/vim -> /etc/alternatives/vim
58 | ```
59 |
60 | 那么如何判断硬链接呢?我们可以试一下。
61 |
62 | ```console
63 | $ touch file
64 | $ ln file hardfile
65 | $ touch other # 无硬链接的文件
66 | $ ls -l
67 | total 0
68 | -rw-r--r-- 2 user user 0 May 3 13:07 file
69 | -rw-r--r-- 2 user user 0 May 3 13:07 hardfile
70 | -rw-r--r-- 1 user user 0 May 3 13:07 others
71 | ```
72 |
73 | 可以注意到,`ls -l` 输出的第二列中,互为硬链接的 `file` 和 `hardfile` 的值为 2。这个值便是对应的文件拥有的硬链接个数。
74 |
75 | 也可以使用 `stat` 来查看。
76 |
77 | ```console
78 | $ stat hardfile
79 | File: hardfile
80 | Size: 0 Blocks: 0 IO Block: 4096 regular empty file
81 | Device: fe01h/65025d Inode: 531077 Links: 2
82 | Access: (0644/-rw-r--r--) Uid: ( 1000/ user) Gid: ( 1000/ user)
83 | Access: 2020-05-03 13:07:06.420556709 +0800
84 | Modify: 2020-05-03 13:07:06.420556709 +0800
85 | Change: 2020-05-03 13:07:15.500656671 +0800
86 | Birth: -
87 | ```
88 |
89 | ## 错误使用 tar 命令导致的文件丢失 {#data-loss-by-tar}
90 |
91 | !!! tip "提示"
92 |
93 | `tar` 的 `-f` 的意义是什么?
94 |
95 | ??? info "解答"
96 |
97 | `*` 被 shell 展开,导致命令变成了这样:
98 |
99 | ```
100 | tar -cf file1 file2 ... target.tar
101 | ```
102 |
103 | 此时,`tar` 会把 `file2` 一直到 `target.tar` 打包的结果放在 `file1` 文件中,于是 `file1` 的内容就被覆盖了。
104 |
105 | ## 为什么 `mv` 命令不需要 `-r` (recursive) 参数 {#why-mv-does-not-need-recursive}
106 |
107 | !!! tip "提示"
108 |
109 | 复制文件夹的过程需要创建新的文件和文件夹,而移动文件夹的操作可以看作是「重命名」。
110 |
111 | ??? info "解答"
112 |
113 | 在移动文件夹时,`mv` 事实上调用了 `rename` 系统调用,即重命名。由于目录是一个树状结构,那么移动文件夹只需要修改这个「文件夹节点」本身的位置与名称即可,不需要对文件夹内的文件(它的子树)做递归的操作。而假使在复制的时候只处理单个「文件夹节点」,那么复制得到的新文件夹中存储的文件和原文件夹存储的文件就会指向相同的文件(而不是再复制一份新的),而这是不符合「复制」这个操作的预期的,所以在复制时,需要递归地复制「文件夹节点」和它的子树,这就是 `mv` 不需要 `-r` 而 `cp`(以及其他类似的操作)需要 `-r` 的原因。
114 |
115 | ## 为什么不建议使用 `apt-key` {#why-not-use-apt-key}
116 |
117 | !!! tip "提示"
118 |
119 | `sudo apt-key add -` 命令将密钥添加到了全局信任。
120 |
121 | ??? info "解答"
122 |
123 | 如果使用 `apt-key` 信任密钥,在私钥泄漏之后,攻击者可以使用泄漏的私钥为其他软件包签名,而信任这对密钥的用户就有可能从其他软件源下载软件包时下载并安装攻击者签名的软件包。
124 |
125 | 一个场景是:
126 |
127 | 1. 你使用 `apt-key` 信任了软件源 A 的密钥;
128 | 2. 软件源 A 的私钥不幸被攻击者 E 得到;
129 | 3. 攻击者 E 成为了你连接网络的中间人,能够截获、修改所有明文的 HTTP 流量;
130 | 4. 攻击者 E 为后门软件 X 打包并签名;
131 | 5. 只要软件源配置中有使用 HTTP 协议的软件源(不一定是软件源 A),攻击者就可以在你安装其他软件包时悄悄将 X 安装到你的机器上。
132 |
133 | 如果单独设置每个软件源信任的密钥,那么造成的攻击面相比之下就小得多了。
134 |
135 | 在 2021 年 12 月,Cloudflare 发表[博客](https://blog.cloudflare.com/dont-use-apt-key/)称其 Cloudflare WARP 软件源的私钥疑似泄漏,并且指出了 `apt-key` 信任密钥带来的可能的安全隐患。此后,一些开源软件(如 Docker)的帮助文档也相应更新。
136 |
--------------------------------------------------------------------------------
/docs/Ch03/supplement.md:
--------------------------------------------------------------------------------
1 | ---
2 | icon: material/puzzle
3 | ---
4 |
5 | # 拓展阅读 {#supplement}
6 |
7 | !!! success "本文已完稿并通过审阅,是正式版本。"
8 |
9 | ## 编译安装 {#compiling-installation}
10 |
11 | 本节[^1]的操作基于 Ubuntu 18.04 操作系统。
12 |
13 | 除了在正文中提到的几种常见的安装方法外,还有从源代码编译安装这种方法,更多地常见于开源软件中。在一个新的平台上(如 amd64),只要有 GCC 编译器,就可以通过编译,快速地将许多常用的软件(如 x86_64 平台上的软件)移植到新的平台上。当然,这并不能解决一些对特定指令集有依赖的软件的移植。
14 |
15 | 本文以编译安装 Nginx 软件作为示范,参考 [NGINX Plus 文档中提供的开源版本 nginx 编译指南](https://docs.nginx.com/nginx/admin-guide/installing-nginx/installing-nginx-open-source/#installing-nginx-dependencies)修改。
16 |
17 | !!! tip "Nginx 软件简介[^2]"
18 |
19 | Nginx 是一个异步框架的网页服务器,也可以用作反向代理、负载均衡器和 HTTP 缓存。
20 |
21 | Nginx 是免费的开源软件,根据类 BSD 许可证的条款发布。大量 Web 服务器使用 Nginx,通常作为负载均衡器。
22 |
23 | ### 安装依赖 {#install-dependencies}
24 |
25 | 在从源代码安装 Nginx 前,需要为它的库安装依赖:
26 |
27 | - PCRE2 - 用于支持正则表达式。
28 |
29 | ```console
30 | $ wget https://github.com/PCRE2Project/pcre2/releases/download/pcre2-10.42/pcre2-10.42.tar.gz
31 | $ tar -zxf pcre2-10.42.tar.gz
32 | $ cd pcre2-10.42
33 | $ ./configure
34 | $ make
35 | $ sudo make install
36 | ```
37 |
38 | ??? tip "PCRE 的版本"
39 |
40 | PCRE 有两个大版本,其中最新的 PCRE2 正在持续维护,而发布于 1997 年的旧版的 PCRE 已经停止维护。PCRE2 的源代码包目前托管于 GitHub 上。相关信息可阅读 [PCRE 官网](https://pcre.org/)。
41 |
42 | - zlib - 用于支持 HTTP 头部压缩。
43 |
44 | ```console
45 | $ wget https://zlib.net/zlib-1.2.13.tar.gz
46 | $ tar -zxf zlib-1.2.13.tar.gz
47 | $ cd zlib-1.2.13
48 | $ ./configure
49 | $ make
50 | $ sudo make install
51 | ```
52 |
53 | - OpenSSL - 用于支持 HTTPS 协议。
54 |
55 | ```console
56 | $ wget https://www.openssl.org/source/openssl-1.1.1c.tar.gz
57 | $ tar -zxf openssl-1.1.1c.tar.gz
58 | $ cd openssl-1.1.1c
59 | $ ./config
60 | $ make
61 | $ sudo make install
62 | ```
63 |
64 | ??? tip "关于 OpenSSL 编译"
65 |
66 | OpenSSL 的编译的有关细节可以参考其[官方 wiki 中的说明](https://wiki.openssl.org/index.php/Compilation_and_Installation)。如果你点击阅读了开头提到的编译文档,可以注意到它使用了 `./Configure darwin64-x86_64-cc --prefix=/usr`,但该指令仅适用于 macOS 操作系统(darwin 为 macOS 操作系统内核名)。
67 |
68 | ### 下载 Nginx 源代码 {#download-source}
69 |
70 | ```console
71 | $ wget https://nginx.org/download/nginx-1.23.3.tar.gz
72 | $ tar -zxf nginx-1.23.3.tar.gz
73 | $ cd nginx-1.23.3
74 | ```
75 |
76 | ### 使用源代码安装 {#install-from-source}
77 |
78 | #### 配置编译选项 {#configure}
79 |
80 | ```console
81 | $ ./configure \
82 | --sbin-path=/usr/local/nginx/nginx \
83 | --conf-path=/usr/local/nginx/nginx.conf \
84 | --pid-path=/usr/local/nginx/nginx.pid \
85 | --with-http_ssl_module \
86 | --with-stream \
87 | --with-pcre=../pcre2-10.42 \
88 | --with-zlib=../zlib-1.2.13 \
89 | --without-http_empty_gif_module
90 | ```
91 |
92 | !!! tip "分解长命令"
93 |
94 | 使用 `\` 符号将一行长命令分解为多行书写,便于阅读。
95 |
96 | #### 编译并安装 {#make-install}
97 |
98 | ```console
99 | $ make
100 | $ sudo make install
101 | ```
102 |
103 | ### 总结 {#summary}
104 |
105 | 从 Nginx 的源代码编译安装可以看出,无论是它的依赖还是主程序,主要过程均经历如下几个步骤:
106 |
107 | 1. 使用 wget 等命令来下载源代码(通常是压缩包),并使用 tar 等命令对其解压。
108 |
109 | 有关 wget 的知识将会在本书[第六章](../Ch06/index.md)进行详细阐述。
110 |
111 | 2. 执行 `./configure` 命令运行 configure 脚本。
112 |
113 | configure 脚本为了让一个程序能够在各种不同类型的机器上运行而设计的。在使用 make 编译源代码之前,configure 会根据自己所依赖的库而在目标机器上进行匹配。configure 文件一般为可执行文件(与权限位有关的将在第五章讲述)。
114 |
115 | configure 脚本可以根据所在的系统环境生成相对应的 Makefile 文件,例如自动处理 GCC 版本、判断特定的函数是否在当前系统上可用、确定相应依赖头文件的位置、并对缺少的依赖库进行报错。
116 |
117 | 在执行 configure 脚本时,可以传递相对应的参数达到生成不同的 Makefile 文件的目的。
118 |
119 | !!! example "configure 脚本的参数"
120 |
121 | 在上面的对 Nginx 源代码进行 `./configure` 时,传入了一些参数,实现了:
122 |
123 | * 指定了一部分配置文件的位置(`sbin-path`、`conf-path`、`pid-path`);
124 | * 说明了需要添加或删除的模块(`http_ssl_module`、`stream`、`pcre`、`zlib`、`http_empty_gif_module`);
125 | * 指定了依赖库的位置(`../pcre2-10.42` 与 `../zlib-1.2.13`)。
126 |
127 | 3. 执行 `make` 命令。
128 |
129 | make 程序会根据 configure 生成的 Makefile 文件,执行一系列的命令,调用 gcc、cc 等程序,将源代码编译为二进制文件。
130 |
131 | 4. 执行 `(sudo) make install` 命令。
132 |
133 | 在这个过程中,make 命令会将编译好的二进制文件拷贝到相对应的安装目录,拷贝用户手册等。
134 |
135 | ## Vim 简介 {#vim}
136 |
137 | Vim 被誉为「编辑器之神」,但是其陡峭的学习曲线也让人望而却步。因为不一定所有的机器上都有 nano,但是可以肯定几乎所有的机器上都会安装 vi(vim 的前身),所以了解如何使用 vim,恐怕是一件难以避免的事情。所幸,vim 的基础操作并不算难。图形界面下也可以安装 `gvim` 获得图形界面。
138 |
139 | 使用 vim 打开文件后,新手会发现自己什么都做不了:不仅无法编辑文件,甚至连 vim 都退出不了。知名的程序员问答社区 Stack Overflow 曾[专门发文庆祝其问答帮助百万开发者退出 vim](https://stackoverflow.blog/2017/05/23/stack-overflow-helping-one-million-developers-exit-vim/)。所以我们首先需要介绍的,是 vim 的两个最常见的模式:普通模式和编辑模式。
140 |
141 | 在打开 vim 后,默认进入的是普通模式,按下 ++"i"++ 就进入编辑模式,进入编辑模式后就可以随意编辑文件了。在这两个模式中,都可以使用键盘方向键移动光标。在编辑完成后,按下 ++"Esc"++ 回到普通模式,然后输入 `:wq` 就可以保存文件并退出;如果不想保存文件直接退出,则输入 `:q!` 即可。
142 |
143 | 以上的简单教学已经可以帮助你正常操作 vim 了,vim 也自带 `vimtutor` 教学程序,可以帮助你快速掌握 vim 的基本操作。熟练使用 vim 有助于提高编辑文本时的工作效率。
144 |
145 | ## 文件的时间戳 {#timestamp}
146 |
147 | 使用 `stat` 工具可以看到一个文件有四个时间戳,分别为 Access,Modify,Change 和 Birth:
148 |
149 | ```console
150 | $ stat test
151 | File: test
152 | Size: 0 Blocks: 0 IO Block: 4096 regular empty file
153 | Device: 801h/2049d Inode: 1310743 Links: 1
154 | Access: (0644/-rw-r--r--) Uid: ( 1000/ ustc) Gid: ( 1000/ ustc)
155 | Access: 2022-02-25 18:12:28.403981478 +0800
156 | Modify: 2022-02-25 18:12:28.403981478 +0800
157 | Change: 2022-02-25 18:12:28.403981478 +0800
158 | Birth: 2022-02-25 18:12:28.403981478 +0800
159 | ```
160 |
161 | ??? info "如何编程获得文件的这四个时间戳?"
162 |
163 | 答案是:使用 Linux 的 `statx()` 系统调用。阅读 `statx(2)` 的 man 文档,可以发现这些信息在返回的结构体中:
164 |
165 | ```c
166 | struct statx {
167 | // 以上内容省略
168 |
169 | /* The following fields are file timestamps */
170 | struct statx_timestamp stx_atime; /* Last access */
171 | struct statx_timestamp stx_btime; /* Creation */
172 | struct statx_timestamp stx_ctime; /* Last status change */
173 | struct statx_timestamp stx_mtime; /* Last modification */
174 |
175 | // 以下内容省略
176 | }
177 | ```
178 |
179 | 访问时间(atime)和创建(Birth, btime)时间很好理解,但是 Modify(mtime)和 Change(ctime)有什么区别呢?可以来试一下:
180 |
181 | ```console
182 | $ stat test
183 | File: test
184 | Size: 0 Blocks: 0 IO Block: 4096 regular empty file
185 | Device: 801h/2049d Inode: 1310743 Links: 1
186 | Access: (0644/-rw-r--r--) Uid: ( 1000/ ustc) Gid: ( 1000/ ustc)
187 | Access: 2022-02-25 18:15:16.625288185 +0800
188 | Modify: 2022-02-25 18:15:16.625288185 +0800
189 | Change: 2022-02-25 18:15:16.625288185 +0800
190 | Birth: 2022-02-25 18:12:28.403981478 +0800
191 | $ vim test # 使用 vim 编辑文件
192 | $ stat test
193 | File: test
194 | Size: 4 Blocks: 8 IO Block: 4096 regular file
195 | Device: 801h/2049d Inode: 1310743 Links: 1
196 | Access: (0644/-rw-r--r--) Uid: ( 1000/ ustc) Gid: ( 1000/ ustc)
197 | Access: 2022-02-25 23:24:29.674464348 +0800
198 | Modify: 2022-02-25 23:24:32.230343236 +0800
199 | Change: 2022-02-25 23:24:32.230343236 +0800
200 | Birth: 2022-02-25 18:12:28.403981478 +0800
201 | $ # mtime 和 ctime(以及 atime)都变化了。
202 | $ chmod +x test # 修改文件权限
203 | $ stat test
204 | File: test
205 | Size: 4 Blocks: 8 IO Block: 4096 regular file
206 | Device: 801h/2049d Inode: 1310743 Links: 1
207 | Access: (0755/-rwxr-xr-x) Uid: ( 1000/ ustc) Gid: ( 1000/ ustc)
208 | Access: 2022-02-25 23:24:29.674464348 +0800
209 | Modify: 2022-02-25 23:24:32.230343236 +0800
210 | Change: 2022-02-25 23:26:45.242321559 +0800
211 | Birth: 2022-02-25 18:12:28.403981478 +0800
212 | $ # 只有 ctime 发生了变化。
213 | ```
214 |
215 | 可以观察到,mtime 仅会在文件数据变化时更新,而 ctime 会在文件数据以及文件元信息(例如权限、所有权)变化时更新。
216 |
217 | 此外,由于 atime 的实际用途不大,有许多用户会在挂载磁盘时添加 `noatime` 参数,让操作系统在访问文件时不去更新 atime,以提高磁盘性能。
218 |
219 | ## tar 的替代与其他压缩软件 {#tar-alternative}
220 |
221 | 在 Linux 上的 tar 一般只支持 gzip、bzip、xz 和 lzip 几种压缩算法,如果需要解压 Windows 上更为常见的 7z、zip 和 rar 等,则需要寻求替代软件。
222 |
223 | ### unar {#unar}
224 |
225 | unar 是 macOS 上的软件 [The Unarchiver](https://theunarchiver.com/) 的命令行工具,能够同样用于 Windows 和 Linux。
226 |
227 | 软件介绍:[Unar and Lsar | Command Line Tools for The Unarchiver](https://theunarchiver.com/command-line)。
228 |
229 | Ubuntu 上直接使用 apt 安装即可:
230 |
231 | ```console
232 | $ sudo apt install unar
233 | ```
234 |
235 | 对于其他 Linux 发行版,请参照网站的介绍,安装合适的依赖以进行编译安装。
236 |
237 | 安装之后会得到两个命令:`unar` 和 `lsar`,分别用来解压存档文件以及浏览存档文件内容:
238 |
239 | ```console
240 | $ unar archive.zip -o output # 将存档文件提取到 output 文件夹中
241 | $ lsar archive.zip # 浏览存档文件内容
242 | $ lsar -l archive.zip # 查看详细信息
243 | $ lsar -L archive.zip # 查看特别详细的信息
244 | ```
245 |
246 | ### 处理 ZIP 压缩包:`zip` 与 `unzip` {#zip}
247 |
248 | `zip` 和 `unzip` 工具分别负责 ZIP 压缩包的压缩与解压缩,使用以下命令安装:
249 |
250 | ```console
251 | $ sudo apt install zip unzip
252 | ```
253 |
254 | 以下提供一些命令例子,更多的功能需要查看对应的文档:
255 |
256 | ```console
257 | $ zip -r archive.zip path/file1 path/dir1 # (递归地)压缩文件和目录
258 | $ zip archive.zip path/file2 # 添加文件到已有的压缩包
259 | $ unzip archive.zip # 解压缩
260 | $ unzip archive.zip -d path/ # 解压缩到指定目录
261 | $ unzip -l archive.zip # 浏览压缩包内容
262 | ```
263 |
264 | ### 处理 RAR 压缩包:`rar` 与 `unrar` {#rar}
265 |
266 | `rar` 和 `unrar` 工具分别负责 RAR 压缩包的压缩与解压缩,使用以下命令安装:
267 |
268 | ```console
269 | $ sudo apt install rar unrar
270 | ```
271 |
272 | !!! warning "RAR 压缩程序的版权问题"
273 |
274 | RAR 的解压缩程序是免费的(源代码也是公开的[^3]),但是压缩程序并不是。Windows 下的 WinRAR,以及上面安装的 Linux 的 `rar` 程序都是 [RARLAB](https://www.rarlab.com/) 的商业共享软件。尽管软件层面没有功能限制,但是根据 RARLAB 的要求,在经过 `rar` 的 40 天试用期后,需要购买授权。具体要求可在安装后查看 `/usr/share/doc/rar/order.htm` 文件。
275 |
276 | 例子如下:
277 |
278 | ```console
279 | $ rar a archive.rar path/file1 path/dir1 # 压缩文件和目录/添加文件和目录到压缩包
280 | $ unrar x archive.rar # 解压缩
281 | $ unrar x archive.rar path/ # 解压缩到指定目录
282 | $ unrar l archive.rar # 浏览压缩包内容
283 | ```
284 |
285 | RARLAB 仅提供了 Linux 下命令行界面的 RAR 压缩包处理工具。但在安装以上软件包后,Linux 下桌面环境中的压缩工具应当都能够支持 RAR 格式的处理。如有特殊需要,可以使用 Wine(Windows 兼容层)运行 WinRAR。
286 |
287 | ### 处理 7zip 等压缩包:`p7zip` {#7z}
288 |
289 | Ubuntu 下 `p7zip-full` 包提供了 `7z` 等工具处理 7z 包(以及其他各种压缩格式):
290 |
291 | ```console
292 | $ sudo apt install p7zip-full
293 | ```
294 |
295 | !!! info "`7z`、`7za`、`7zr` 与 `p7zip` 的区别"
296 |
297 | `p7zip-full` 软件包同时提供了以上的命令行工具。其中 `7z`、`7za` 和 `7zr` 都是直接处理 7zip 压缩包的程序,区别如下[^4]:
298 |
299 | - `7z`:通过插件支持各类压缩格式的处理;
300 | - `7za`:是独立的程序,支持的格式比 `7z` 少一些;
301 | - `7zr`:轻量级的 `7za`,仅包含 7zip 等少量压缩算法支持的工具。
302 |
303 | 而 `p7zip` 基于 `7za` 或 `7zr`,提供类似于 `gzip` 的接口。
304 |
305 | 以下给出 `7z` 命令行工具的一些例子:
306 |
307 | ```console
308 | $ 7z a archive.7z path/file1 path/dir1 # 压缩文件和目录/添加文件和目录到压缩包
309 | $ 7z x archive.7z # 解压缩
310 | $ 7z x archive.7z -opath/ # 解压缩到 path/ 目录下
311 | $ 7z l archive.7z # 浏览压缩包内容
312 | ```
313 |
314 | 与 `rar` 类似,7z 未提供 GUI 工具。如有特殊需要,可以使用 Wine 运行 Windows 版的 7-Zip。
315 |
316 | ## 引用来源与备注 {#references .no-underline }
317 |
318 | [^1]: 本节使用的示例参考自 Nginx 官方说明 [Compiling and Installing from Source](https://docs.nginx.com/nginx/admin-guide/installing-nginx/installing-nginx-open-source/#compiling-and-installing-from-source)。
319 | [^2]: 信息来自维基百科条目:[Nginx](https://zh.wikipedia.org/wiki/Nginx)。
320 | [^3]: 但是 `unrar` 不是开源软件,因为它的[协议](https://github.com/debian-calibre/unrar-nonfree/blob/master/license.txt)不允许使用其代码制作压缩 RAR 包的工具,这违背了开源软件的定义。
321 | [^4]: 参考了 与相关 man 文档编写。
322 |
--------------------------------------------------------------------------------
/docs/Ch04/images/PID.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch04/images/PID.png
--------------------------------------------------------------------------------
/docs/Ch04/images/abstract.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch04/images/abstract.png
--------------------------------------------------------------------------------
/docs/Ch04/images/add_column.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch04/images/add_column.png
--------------------------------------------------------------------------------
/docs/Ch04/images/bg.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch04/images/bg.gif
--------------------------------------------------------------------------------
/docs/Ch04/images/bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch04/images/bg.png
--------------------------------------------------------------------------------
/docs/Ch04/images/crontab.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch04/images/crontab.gif
--------------------------------------------------------------------------------
/docs/Ch04/images/fg_bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch04/images/fg_bg.png
--------------------------------------------------------------------------------
/docs/Ch04/images/forking.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch04/images/forking.png
--------------------------------------------------------------------------------
/docs/Ch04/images/htop.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch04/images/htop.gif
--------------------------------------------------------------------------------
/docs/Ch04/images/privilege.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch04/images/privilege.png
--------------------------------------------------------------------------------
/docs/Ch04/images/services.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch04/images/services.png
--------------------------------------------------------------------------------
/docs/Ch04/images/signal_slide.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch04/images/signal_slide.png
--------------------------------------------------------------------------------
/docs/Ch04/images/state.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch04/images/state.png
--------------------------------------------------------------------------------
/docs/Ch04/images/test.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch04/images/test.gif
--------------------------------------------------------------------------------
/docs/Ch04/images/time_slice.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch04/images/time_slice.png
--------------------------------------------------------------------------------
/docs/Ch04/images/tmux.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch04/images/tmux.gif
--------------------------------------------------------------------------------
/docs/Ch04/images/top8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch04/images/top8.png
--------------------------------------------------------------------------------
/docs/Ch04/images/type_kill.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch04/images/type_kill.png
--------------------------------------------------------------------------------
/docs/Ch04/solution.md:
--------------------------------------------------------------------------------
1 | ---
2 | icon: material/tooltip-question
3 | ---
4 |
5 | # 思考题解答 {#solution}
6 |
7 |
16 |
17 | ## 按下 Ctrl + C 后发生了什么 {#after-hitting-ctrl-c}
18 |
19 | !!! tip "提示"
20 |
21 | 这个问题涉及了键盘按键的处理与信号机制。(答案仅供参考)
22 |
23 | ??? info "解答"
24 |
25 | 当我们在某个终端按下键盘上的 `Ctrl + C`,键盘发生按下 Ctrl——按下字母 C——字母 C 按键抬起——Ctrl 键抬起四个过程,将四个过程对应的扫描码送到键盘控制器。
26 |
27 | 键盘控制器向 CPU 发出中断表示键盘有输入需要处理,CPU 调用对应的处理程序(一般是终端驱动)将扫描码翻译为键盘码,对应的字符如果允许,会回显到标准输出(显示器)。
28 |
29 | 如果是普通字符,则字符存放到对应终端的缓冲区等待读取。
30 |
31 | 如果像 `Ctrl + C` 这种特殊字符被检测到,则该处理程序向该终端上的 shell 进程发送 SIGINT,shell 再向其前台进程转发 SIGINT,进程接到该信号,执行对应的信号处理例程。一般情况下,程序将正常退出。
32 |
33 | 我们可以使用 `stty -a` 命令列出终端驱动所识别的一些具有特殊含义的字符。注意该命令必须于真正的终端(使用 `Ctrl + Alt + Fn` 切换得到的终端)才可以使用。
34 |
35 | ## SIGKILL(`kill -9`)可以杀死所有进程吗? {#sigkill-effectiveness}
36 |
37 | ??? info "解答"
38 |
39 | 1. 状态为 D(Disk sleep)与 Z(Zombie)的进程收到 SIGKILL 信号后会发生什么?
40 |
41 | 处于 D 状态的进程正在执行不可中断的系统调用(或者说正在执行无法被中断的内核代码)中,因此发送 SIGKILL 信号之后,需要等待它完成该系统调用,返回内核调度器时才会被杀死。
42 |
43 | 处于 Z 状态的进程事实上已经不再执行,所以发送 SIGKILL 信号也不会让它再退出。这种情况一般是它的父进程没有妥善处理好生成的子进程。有时候将其父进程杀死后,子进程会被转交给 init 进程。绝大多数情况下,init 进程都能够恰当处理这个问题,将该进程移除。
44 |
45 | 2. 可以向 1 号进程 init 发送 SIGKILL 信号吗?发送之后会发生什么?
46 |
47 | 可以发送,但是会被操作系统(内核)忽略,因此 init 不会被 SIGKILL 杀死。
48 |
49 | ## `nohup` 合适吗? {#nohup-suitableness}
50 |
51 | !!! tip "提示"
52 |
53 | 如果同学的 web 程序崩溃了会发生什么?如果机器重启了呢?
54 |
55 | ??? info "解答"
56 |
57 | Nohup 是非常方便的工具,但是对于运行需要长时间服务的程序来讲并不可靠。如果出现程序崩溃、系统重启等情况,用 nohup 启动的「服务」不会自动重启。
58 |
59 | 可以将这个 web 程序配置为 systemd service。如果没有对应服务器的 root 权限,也可以使用 systemd user service,或者使用诸如 supervisor 等工具运行服务程序。
60 |
61 | ## 多人使用的 tmux {#multiuser-tmux}
62 |
63 | !!! tip "提示"
64 |
65 | 使用会话(Session)。
66 |
67 | ??? info "解答"
68 |
69 | 在启动 tmux 时,可以为启动的会话命名:
70 |
71 | ```console
72 | $ tmux new -s session-a # 启动名为 session-a 的 tmux 会话
73 | ```
74 |
75 | 脱离 tmux 之后如果还需要再进入这个会话,使用以下命令即可:
76 |
77 | ```console
78 | $ tmux a -t session-a
79 | ```
80 |
81 | 对于实验室这个场景,要防止不同用户的操作互相影响,与其他同学提前约定好使用不同的会话名即可。
82 |
83 | ## 服务日志 {#journal}
84 |
85 | !!! tip "提示"
86 |
87 | 使用 `journalctl` 查看日志。
88 |
89 | ??? info "解答"
90 |
91 | 1. 某个服务的日志
92 |
93 | `journalctl` 的 `-u` 参数可以指定 unit,例子如下:
94 |
95 | ```console
96 | $ sudo journalctl -u ssh # 查看 ssh 服务的日志
97 | ```
98 |
99 | 2. 某个**正在运行**的服务**正在**输出的日志
100 |
101 | 在第一条的基础上,可以指定 `-f` 参数,以获取正在输出的日志:
102 |
103 | ```console
104 | $ sudo journalctl -u ssh -f
105 | ```
106 |
107 | 按下 Ctrl + C 退出输出即可。
108 |
109 | 3. 系统**正在**输出的日志
110 |
111 | ```console
112 | $ sudo journalctl -f
113 | ```
114 |
115 | 系统日志对于调试系统状态来说是非常有用的信息。
116 |
117 | 4. 上一次启动到关机的所有日志
118 |
119 | ```console
120 | $ sudo journalctl -b -1
121 | ```
122 |
123 | `-b` 参数表示 boot(启动),`-1` 表示上一次启动。如果不添加 `-1` 参数,则默认为当前启动的日志。
124 |
125 | ## 耗时的定时任务 {#time-consuming-crontab}
126 |
127 | ??? info "解答"
128 |
129 | Crontab 做的事情就是定时执行任务,它不会去管上一次执行的任务是否已经完成(维护状态也有一定的开销)。设想这样一种情况:
130 |
131 | 1. 你使用 crontab 设置了一个每日 rsync 任务(rsync 是用于机器间同步文件的工具),将远程的机器上的一些数据同步到你的机器上;
132 | 2. 有一天,机器突然断网了。此时 crontab 仍然会启动 rsync。由于默认不超时,所以 rsync 会等待网络建立、同步成功后才会退出;
133 | 3. 一个月之后,你发现这台机器断网了,修好之后,你发现同时有 30 个 rsync 进程在执行同步操作,这会对网络以及磁盘都带来很大的压力。
134 |
135 | Systemd timer 则会检查任务(timer 对应的服务)是否在执行,如果在执行,就不会产生新的进程。
136 |
137 | 如果仍然需要使用 crontab,那么可以考虑使用 `flock`。首先指定一个「锁」文件,然后将命令改成这样:
138 |
139 | ```
140 | flock -n [锁文件的路径] [你需要执行的命令]
141 | ```
142 |
143 | 这样的话,如果其他命令正在执行,那么这个文件锁会被其占用;crontab 尝试再执行命令时,flock 会发现对应的文件已经锁上,因此会立刻退出(`-n` 参数)。
144 |
--------------------------------------------------------------------------------
/docs/Ch04/supplement.md:
--------------------------------------------------------------------------------
1 | ---
2 | icon: material/puzzle
3 | ---
4 |
5 | # 拓展阅读 {#supplement}
6 |
7 | !!! Failure "本文目前尚未完稿,存在诸多未尽章节且未经审阅,不是正式版本。"
8 |
9 | ## 进程组织结构 {#process-struct}
10 |
11 | ### 进程父子关系 {#parent-child}
12 |
13 | Fork 是类 UNIX 中创建进程的基本方法:将当前的进程完整复制一份。新进程和旧进程唯一的区别是 `fork()` 的返回值不同。程序员可以根据其返回值为新旧进程设置不同的逻辑。
14 |
15 | 除了最开始的 0 号进程外,绝大多数情况下其他进程是由另一个进程通过 fork 产生的。这里产生进程的一方为**父进程**,被产生的是**子进程**。在 Linux 中,父进程可以等待子进程,接收子进程退出信号以及返回值。
16 |
17 | ??? tip "孤儿进程 (orphan) 和僵尸进程 (zombie)"
18 |
19 | 父子关系引出了两种特殊的运行情况——父进程先退出,它的子进程成为**孤儿进程** (orphan);子进程先退出,而父进程未作出回应 (wait),子进程则变为**僵尸进程** (zombie)。
20 |
21 | 孤儿进程(即留下的子进程)由操作系统回收,交给 init「领养」。
22 |
23 | 僵尸进程的进程资源大部分已释放,但占用一个 PID,并保存返回值。系统中大量僵尸进程的存在将导致无法创建进程。
24 |
25 | 我们可以使用 htop 查看进程的父进程等信息。按 F2,随后可以自主选择进程的属性列在面板上,以 Parent PID 为例 (PPID),点击 colomns,点击 PPID,注意到下方提示按 F5 可以添加到左侧,再依照下方提示调整顺序。同理可以顺便在 PPID 后顺序添加 PGRP,TTY_NR,TPGID,SESSION 等列以便观察下面的实验结果。
26 |
27 | 
28 |
29 | 如果 F10 被终端程序占用了,可以尝试用鼠标点击选项,或者修改终端程序的设置。例如,Xfce 默认的终端程序可以在 设置 => 高级 中取消对 F10 的占用。
30 |
31 | ### 进程组 {#pgroup}
32 |
33 | **进程组**大体上是执行同一工作的进程形成的一个团体,通常是由于父进程 fork 出子进程后子进程继承父进程的组 ID 而逐渐形成。设计进程组机制主要是为了面向协作任务,比如 Firefox 工作是网页浏览,那么其相关的进程一定属于一个进程组。进程组的出现方便了系统信号管理,后面可以看到,发给一个进程组的信号将被所有属于该组的进程接收,意义就是停止整个任务整体。
34 |
35 | ### 会话——前台与后台 {#session}
36 |
37 | 而**会话** (session) 可以说是面向用户的登录出现的概念。当用户从终端登录进入 shell,就会以该 shell 为会话首进程展开本次会话。一个会话中通常包含着多个进程组,分别完成不同的工作。用户退出时,这个会话会结束,但有些进程仍然以该会话标识符 (session ID) 驻留系统中继续运行。
38 |
39 | 说到会话,就必然涉及到 Linux 会话中的前后台管理机制。**前台** (foreground) 与**后台** (background),本质上决定了是否需要与用户交互,对于单独的一个 shell,只能有一个前台进程(组),其余进程只能在后台默默运行,上述中若干进程组,正是前台进程组和后台进程组的概称。在稍后部分中我们将学习前后台切换的相关操作。
40 |
41 | 总结:
42 |
43 | | 进程属性 | 意义/目的 |
44 | | :------: | ------------------------------------------------------------------------------------------------------------- |
45 | | PID | Process ID,标识进程的唯一性。 |
46 | | PPID | Parent PID,标识进程父子关系。 |
47 | | PGID | Process Group ID,标识共同完成一个任务的整体。 |
48 | | TPGID | 标识一组会话中处于前台(与用户交流)的进程(组)。 |
49 | | SID | Session ID,标识一组会话,传统意义上标识一次登录所做的任务的集合,如果是与具体登录无关的进程,其 SID 被重置。 |
50 |
51 | ## 守护进程的产生 {#daemon-creation}
52 |
53 | 许多守护进程直接由命令行的 shell 经 fork 产生,这样的进程首先要脱离当前会话,否则父进程退出时子进程也会退出。创建会话的系统调用是 `setsid()`。然而从 shell 中 fork 出来的进程为进程组组长,不能调用 setsid 另开会话:
54 |
55 | ```
56 | DESCRIPTION
57 | setsid() creates a new session if the calling process is not a process group leader. The calling
58 | process is the leader of the new session (i.e., its session ID is made the same as its process
59 | ID). The calling process also becomes the process group leader of a new process group in the
60 | session (i.e., its process group ID is made the same as its process ID).
61 |
62 | The calling process will be the only process in the new process group and in the new session.
63 |
64 | Initially, the new session has no controlling terminal. For details of how a session acquires a
65 | controlling terminal, see credentials(7).
66 | ```
67 |
68 | 所以自身创建子进程后退出,子进程调用 setsid 脱离会话,自身成为会话组组长。此时大部分守护进程已初步形成。
69 |
70 | 实际上,如果我们使用类似 `bash -c "ping localhost &" &` 这样的命令就可以模拟守护进程创建的过程:首先现有 shell 创建了 bash 作为子进程,该 bash 将 `ping localhost` 放入后台执行。由于不是交互模式,没有前台进程 bash 将自动退出。该 bash 的后台进程甚至不需要退出 session,就可以不受 SIGHUP 的影响。未 setsid 的 ping 命令可以一直在该终端输出,可见退出 session 的意义在于放弃该 tty。
71 |
72 | 打开 htop,按 PID 顺序排列,排在前面的用户进程历来都是守护进程,它们大多数先于用户登录而启动。可以注意到,守护进程的 SID 与自身 PID 相同。
73 |
74 | ## Linux 下进程查看原理 {#proc}
75 |
76 | 正文提到,ps 做为查看进程的基本命令,仅仅提供静态输出,并不能提供实时监控。但是它足够简单,可以供我们进行分析。直接阅读 ps 的源代码是最直接的方法,但是成本可能过高,更好的方法是用 strace 命令来分析 ps 运行过程中使用到的系统调用。
77 |
78 | !!! info "什么是系统调用"
79 |
80 | 现代操作系统有效地隔离了进程,不允许进程越过操作系统与外界交互。操作系统提供的和外界交互的方式就是系统调用。操作系统提供的系统调用可以让进程对文件、内存、进程等的状态进行控制,从而在安全隔离进程的前提下允许程序实现丰富多彩的功能。
81 |
82 | 系统调用运行在操作系统核心,为内核与用户层提供了一种通信的方式,是各用户进程「使用」操作系统的统一接口。如果没有系统调用,用户程序就需要直接操作硬件来进行需要的操作,而这对于现代操作系统来说显然是无法接受的。
83 |
84 | !!! info "strace 命令"
85 |
86 | strace 可以追踪程序使用的系统调用,输出在屏幕上,是一个程序调试工具。此处用来追踪 ps 打开的文件。
87 |
88 | strace 开头字母为 s 是由于该命令为 Sun™ 系统移植而来的调用追踪程序。
89 |
90 | 注意 strace 会输出到标准错误 (stderr),需要将输出重定向到标准输出之后通过管道后才能使用 grep 等工具。关于重定向、管道等内容,可以查看[第六章](../Ch06/index.md#redirection-and-pipe)。
91 |
92 | ```console
93 | $ strace ps
94 | ...
95 | openat(AT_FDCWD, "/proc/1/stat", O_RDONLY) = 6
96 | read(6, "1 (systemd) S 0 1 1 0 -1 4194560"..., 1024) = 193
97 | close(6) = 0
98 | openat(AT_FDCWD, "/proc/1/status", O_RDONLY) = 6
99 | read(6, "Name:\tsystemd\nUmask:\t0000\nState:"..., 1024) = 1024
100 | read(6, "00,00000000,00000000,00000000,00"..., 1024) = 295
101 | close(6)
102 | ...
103 | ```
104 |
105 | `strace` 会输出很多内容,上面是其中的典型案例。
106 |
107 | 可以大致猜测,ps 通过打开 `/proc/1` 文件夹下的 `stat` 和 `status` 文件,获得 1 号进程的信息。我们也可以试着打开它:
108 |
109 | ```console
110 | $ cat /proc/1/stat # 由于用户权限不同,是否添加 sudo 会导致读取出不同内容
111 | 1 (systemd) S 0 1 1 0 -1 4194560 113722 4652720 87 2258 79 670 19018 28647 \
112 | 20 0 1 0 4 231030784 2252 18446744073709551615 1 1 0 0 0 0 671173123 4096 1260 \
113 | 0 0 0 17 0 0 0 135 0 0 0 0 0 0 0 0 0 0
114 |
115 | $ cat /proc/1/status
116 | Name: systemd
117 | Umask: 0000
118 | State: S (sleeping)
119 | Tgid: 1
120 | Ngid: 0
121 | Pid: 1
122 | PPid: 0
123 | TracerPid: 0
124 | Uid: 0 0 0 0
125 | Gid: 0 0 0 0
126 | FDSize: 256
127 | Groups:
128 | NStgid: 1
129 | NSpid: 1
130 | NSpgid: 1
131 | (以下内容省略)
132 | ```
133 |
134 | 也许第一个文件不是那么好看,但第二个文件就很直白了。至此可以得出结论,根目录下 `/proc` 文件夹储存进程信息,而 htop 等命令通过对该文件夹下的文件进行自动读取来监视进程。实际上,`/proc` 是一个虚拟的文件系统,存在于内存中,反映着系统的运行状态。
135 |
136 | ## SysRq: 进行紧急的系统维护操作 {#sysrq}
137 |
138 | 你可能会注意到,你的键盘上好像有一个从未使用过的键:SysRq。其实它在 Linux 上可以对内核进行一些操作,尤其是在紧急的情况下(例如,界面卡死),可以用来关闭进程、干净地(在不损坏文件系统的情况下)重启系统等操作。
139 |
140 | 执行 `cat /proc/sys/kernel/sysrq` 可以查看这个功能是否启用,如果是 1 的话,就可以使用 SysRq 键了,否则需要 root 用户执行 `echo 1 > /proc/sys/kernel/sysrq` 以启用所有 SysRq 功能。按住 Alt + SysRq,再按下其他特定的按键,就可以执行特定的功能。
141 |
142 | 一个口诀是 "BUSIER",反过来就是 "REISUB",是一套可以(尽可能在)在操作界面无响应的时候干净地重启系统的按键。按住 Alt + SysRq 后依次按下这六个键即可。
143 |
144 | - R: 从 X 桌面环境夺回键盘的控制权。
145 | - E: 向除了 init (PID = 1) 以外的进程发送 SIGTERM 信号,要求它们干净地退出。
146 | - I: 向除了 init 以外的进程发送 SIGKILL 信号,强制退出。
147 | - S: 从内存同步文件修改到文件系统。
148 | - U: 重新挂载所有的文件系统为只读状态。
149 | - B: 立刻重启系统。
150 |
151 | ## 关于 `fork()` {#fork}
152 |
153 | 通过以下实验,我们可以尝试使用 fork 系统调用体验建立父子进程关系。
154 |
155 | 程序文件 `forking.c`:
156 |
157 | ```c
158 | #include
159 | #include // Unix standard header,提供 POSIX 标准 API
160 |
161 | int main() {
162 | for (int i = 0; i < 3; i++)
163 | {
164 | int pid = fork(); // fork 系统调用,全面复制父进程所有信息。
165 | if (pid == 0) {
166 | // 子进程返回 pid=0。
167 | printf("I'm child, forked in %d turn\n", i);
168 | } else if (pid < 0) {
169 | // fork 失败,pid 为负值。
170 | printf("%d turn error\n", i);
171 | } else {
172 | // 父进程返回子进程 pid。
173 | printf("I'm father of %d turn, child PID = %d\n", i, pid);
174 | }
175 | sleep(3);
176 | }
177 | sleep(1000);
178 | return 0;
179 | }
180 | ```
181 |
182 | 随后,在文件所在目录下打开 shell,运行 `gcc forking.c -o forking && ./forking`,就可以在另一终端打开 htop 查看成果了。
183 |
184 | 
185 |
186 | 按下 T 键,界面显示的进程将转化为树状结构,直观描述了父子进程之间的关系。此处可以明显观察到树梢子进程的 PID 等于父进程的 PPID。
187 |
188 | 同时由 shell 进程创立的 forking 进程的进程组号 (PGRP) 为自己的 PID,剩余进程的 PGRP 则继承自最开始的 forking 进程,PGRP 可以通过系统调用修改为自身,从原进程组中独立出去另起门户。
189 |
190 | 接下来会看到进程 SID 一律为该进程的控制 shell 的 PID。
191 |
192 | !!! question "问题"
193 |
194 | 上述实验中,输入 `./forking` 后一共产生了多少个进程呢,可以不看 htop 就推算出来吗?
195 |
196 | ## 编程处理信号 {#signal-programming}
197 |
198 | 你可能会注意到,有些程序对你按下 Ctrl + C 的操作会有一些独特的响应,例如 `ping`,如果使用 Ctrl + C 键盘中断 (SIGINT),在程序终止之前会有一段总结;而使用 SIGTERM 不会有此效果。
199 |
200 | 这个实验中,我们使用系统调用 `signal()` 来重新设置进程对信号的响应函数。
201 |
202 | 程序文件 `signal_handle.c`:
203 |
204 | ```c
205 | #include
206 | #include // 定义了变更信号处理函数的方法以及一些信号对应的常量(如 #define SIGTERM 15)
207 | #include // sleep 函数
208 |
209 | void sig_handler(int sig); // 设置一个处理信号的函数
210 |
211 | int main() {
212 | signal(SIGTERM, sig_handler); // 替换默认终止信号处理例程
213 | // signal(SIGINT, sig_handler); // 替换键盘中断(keyboard interrupt)处理例程
214 | // signal(SIGHUP, sig_handler); // 替换控制进程挂起信号处理例程
215 | // signal(SIGKILL, sig_handler); // 替换……不存在的!
216 |
217 | while (1) {
218 | sleep(10); // do something
219 | }
220 | }
221 |
222 |
223 | void sig_handler(int sig) {
224 | printf("hi!\n"); // 在收到信号时输出
225 | // fflush(stdout); // 如果你的输出内容不包括回车,或许需要刷新缓冲区才能看到效果。因为标准输出是按行缓冲的。
226 | }
227 | ```
228 |
229 | 随后,在文件所在目录下打开 shell,运行 `gcc signal_handle.c -o signal_handle && ./signal_handle`,就可以在另一终端打开 htop 来试验了。
230 |
231 | !!! warning "可重入性"
232 |
233 | 事实上,这个程序存在一个隐含的问题:信号输入后,程序的执行流变成了 `sig_handler()`,在处理函数执行完成之后,原来的程序是否还能正常运行?
234 |
235 | 这就牵扯到「可重入性」(reentrant) 这个概念了。如果某个函数可以在任意时刻被中断,并且这个函数在中断返回之前又再次被中断处理程序执行而不会出现错误,那么它就是「可重入」的。信号处理函数应当可重入,以保证安全执行。不是所有的函数都是可重入的,访问 `man signal-safety`,可以查看到一份可重入库函数的列表。
236 |
237 | 但是很遗憾,`printf()` 不是可重入的:如果程序正在执行 `printf()` 的时候有信号输入,处理函数运行 `printf()` 会导致输出缓冲区的数据更新,之后回到原程序的 `printf()` 的时候,就有可能出现问题。
238 |
239 | ## 终端 (Terminal) 与控制台 (Console) {#terminal}
240 |
241 | 在上世纪六十年代,个人计算机尚未开始发展,用户使用计算机的一种常见方式就是通过终端,与远程的服务器连接交互。当时键盘和显示器连为一体,称为终端(terminal)。而主机自带的一套键盘与屏幕只能给系统管理员使用,称为控制台 (console),用来输出启动 debug 信息(现在的 Linux 系统如果因故障而不得不进入单用户修复模式,则只有一个终端 `/dev/console` 开启)。
242 |
243 | 然而随着时代的发展,这种模式逐渐被家庭电脑的分布式主机取代,我们不需要,也没有多套终端了,只有显示器、键盘、鼠标。但是为了向前兼容性,我们需要假装这是一个(甚至多个)终端,所以一般发行版 `/dev` 目录下有 7 个终端 `tty1 ~ tty7`,通过 `Ctrl + Alt + F1 ~ F7` 切换键盘与显示器与哪个终端相对应。
244 |
245 | 再后来,随着时代发展,终端需要出现在图形界面上了,然而承载图形界面的也是终端,所以终端里的终端就需要终端模拟器来实现了。由此,出现在图形界面上的终端才叫终端模拟器。
246 |
247 | 没有图形界面时,shell 一般为控制台 (tty) 的子进程,在图形界面上 shell 建立在虚拟终端 (pty, pseudo tty) 之上。顺带一提,服务器常用的远程连接工具 `ssh` 的父进程也是一个 pty。
248 |
249 | **注意:终端不是 Shell,尽管它们经常被弄混淆。**
250 |
251 | 参考阅读: [你真的知道什么是终端吗?](https://www.linuxdashen.com/%E4%BD%A0%E7%9C%9F%E7%9A%84%E7%9F%A5%E9%81%93%E4%BB%80%E4%B9%88%E6%98%AF%E7%BB%88%E7%AB%AF%E5%90%97%EF%BC%9F)
252 |
253 |
268 |
--------------------------------------------------------------------------------
/docs/Ch05/assets/unix_filesystem.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch05/assets/unix_filesystem.png
--------------------------------------------------------------------------------
/docs/Ch05/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | icon: material/account-group
3 | ---
4 |
5 | # 用户与用户组、文件权限、文件系统层次结构
6 |
7 | !!! success "本文已完稿并通过审阅,是正式版本。"
8 |
9 | !!! abstract "导言"
10 |
11 | 很多 Linux 的初学者都会对以下这些问题感到迷惑:
12 |
13 | * 为什么我改个设置,装个软件,动不动就要我输密码?
14 | * 为什么有些命令要 `sudo`,有些不要?
15 | * 为什么我运行不了网上下载的程序?为什么系统里面有些目录我看不了?
16 | * 我的 C 盘、D 盘跑哪里去了?根目录 (`/`) 里面一堆 `etc`, `usr` 是什么玩意啊?
17 |
18 | 以下内容将会解答你的疑问。
19 |
20 | ## 用户与用户组 {#users-and-groups}
21 |
22 | ### 为何需要「用户」 {#why-user}
23 |
24 | 早期的操作系统没有用户的概念(如 MS-DOS),或者有「用户」的概念,但是几乎不区分用户的权限(如 Windows 9x)。而现在,这不管对于服务器,还是个人用户来说,都是无法接受的。
25 |
26 | 在服务器环境中,「用户」的概念是明确的:服务器的管理员可以为不同的使用者创建用户,分配不同的权限,保障系统的正常运行;也可以为网络服务创建用户(此时,用户就不再是一个有血有肉的人),通过权限限制,以减小服务被攻击时对系统安全的破坏。
27 |
28 | 而对于个人用户来说,他们的设备不会有第二个人在使用。此时,现代操作系统一般区分使用者的用户与「系统用户」,并且划分权限,以尽可能保证系统的完整性不会因为用户的误操作或恶意程序而遭到破坏。
29 |
30 | ### Linux 下的用户简介 {#intro-user-under-linux}
31 |
32 | 你可以查看 `/etc/passwd` 文件,来得到系统中用户的配置信息。
33 |
34 | !!! example "`/etc/passwd` 示例"
35 |
36 | 以下是一个例子:
37 |
38 | ```
39 | root:x:0:0:root:/root:/bin/bash
40 | daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
41 | bin:x:2:2:bin:/bin:/usr/sbin/nologin
42 | (中间内容省略)
43 | sshd:x:110:65534::/run/sshd:/usr/sbin/nologin
44 | ustc:x:1000:1000:ustc:/home/ustc:/bin/bash
45 | lxd:x:998:100::/var/snap/lxd/common/lxd:/bin/false
46 | mysql:x:111:116:MySQL Server,,,:/nonexistent:/bin/false
47 | ```
48 |
49 | 在此文件中,每一行都代表一个用户,每行中用户信息由冒号 `:` 隔开,存储着包括用户名、用户编号 (UID, User ID)、家目录位置等信息。更多介绍,可以通过 `man 5 passwd` 查阅。
50 |
51 | 可以关注到,除了你自己以外,还有一个特殊的用户:`root`,和一大堆你素未相识的名字。下面将会进行介绍。
52 |
53 | ??? tip "小知识:`/etc/passwd` 作用的变化"
54 |
55 | 在 Unix 最初的时候,`passwd` 文件存储了用户密码的哈希[^1]。但是,这个文件是所有用户都可以读取的。为了不让用户的密码哈希被任意获取,进而导致用户的密码被暴力破解,现在一般把密码存在别的地方。对于 Linux 来说,密码哈希信息存储在 `/etc/shadow` 里面,只有下文提到的根用户可以访问与修改。
56 |
57 | #### 根用户 {#root-user}
58 |
59 | 在前文中我们知道,在使用 `apt` 安装软件时,我们需要在前面加上 `sudo`。这可以使我们以根用户 (`root`) 的身份安装软件。
60 |
61 | 根用户 / `root` 用户在 Linux 操作系统中拥有最高的权限,可以对系统做任何操作(包括删除所有系统文件这一类**极端危险的操作**)。`root` 用户的用户数据存储在 `/root` 下。
62 |
63 | 在我们使用 `sudo` 的时候,输入自己的密码并验证正确之后,`sudo` 就会以 `root` 用户的身份,执行后面我们希望执行的命令。而使用 `apt` 安装的软件存储在了系统的目录下,所以必须要以 `root` 用户的身份安装。这就是我们平时需要 `sudo` 来安装软件的原因。
64 |
65 | !!! danger "谨慎使用 `root` 用户权限执行命令!"
66 |
67 | 我们知道,`root` 用户可以对系统做极其危险的操作。当使用 `root` 权限执行命令时(如使用 `sudo`),一定要**小心、谨慎,理解命令的含义之后再按下回车**。**请不要复制网络上所谓的「Linux 优化命令」等**,以 `root` 权限执行,否则**可能会带来灾难性的后果**。除了复制外,直接通过 `curl` 等工具获取脚本然后通过管道传给 `sh` 执行也是非常危险的操作。运行脚本前,请务必先仔细检查要执行的脚本内容。
68 |
69 | 以下是一些会对系统带来毁灭性破坏的例子。 **再重复一遍,不要执行下面的命令!**
70 |
71 | * `rm -rf /`(删除系统中的所有可以删除的文件,**包括被挂载的其他分区**。**即使不以 `root` 权限执行,也可以删掉自己的所有文件。**)
72 | * `mkfs.ext4 /dev/sda`(将系统的第一块硬盘直接格式化为 ext4 文件系统。这会破坏其上所有的文件。)
73 | * `dd if=/dev/urandom of=/dev/sda`(对系统的第一块硬盘直接写入伪随机数。这会破坏其上所有的文件,并且找回文件的可能性降低。)
74 | * `:(){ :|: & };:`(被称为「Fork 炸弹」,会消耗系统所有的资源。在未对进程资源作限制的情况下,只能通过重启系统解决,所有未保存的数据会丢失。)
75 |
76 | #### 系统用户 {#system-user}
77 |
78 | 除了你、`root` 和其他在用你的电脑/服务器的人(如果有)以外,剩下还有很多用户,如 `nobody`, `www-data` 等。它们由系统或相关程序创建,用于执行服务等系统任务。**不要随意删除这些用户**,以免系统运行出现问题。
79 |
80 | ??? tip "技术层面上[^2],系统用户和普通用户的区别"
81 |
82 | 一般地,在 Linux 中,系统用户的 UID 有一个指定范围,而这段范围在各个发行版中可能不同。如 Debian 使用了 100-999, 60000-64999 等区间分配给系统用户[^3]。
83 |
84 | 此外,由于系统用户的特殊性,它们一般默认禁止使用密码登录。
85 |
86 | #### 普通用户 {#normal-user}
87 |
88 | 普通用户可以登录系统,并对自己的家目录下的文件进行操作。所有普通用户的家目录都在 `/home/` 下,位于 `/home/username/` 的位置,其中 `username` 是用户名。
89 |
90 | 普通用户无法直接修改系统配置,也无法为系统环境安装或卸载软件。
91 |
92 | ### 切换用户:使用 `su` 和 `sudo` {#using-su-and-sudo}
93 |
94 | #### `sudo` {#sudo}
95 |
96 | `sudo` 命令可以让你以另一个用户的身份执行指定的命令。当然,它最常见的用途,就是能让普通用户以 `root` 的身份执行命令:不加入其他参数,`sudo` 后面直接加命令,我们在前面的课程中也见到很多次了。
97 |
98 | ??? tip "以 `root` 用户执行上一条命令"
99 |
100 | 你是否常常忘记敲 `sudo`,结果还要把后面的整条命令重新敲一遍?在发现权限不足之后有一个方便的「补救方案」:`sudo !!`,效果如下:
101 |
102 | ```console
103 | $ apt update
104 | Reading package lists... Done
105 | E: Could not open lock file /var/lib/apt/lists/lock - open (13: Permission denied)
106 | E: Unable to lock directory /var/lib/apt/lists/
107 | W: Problem unlinking the file /var/cache/apt/pkgcache.bin - RemoveCaches (13: Permission denied)
108 | W: Problem unlinking the file /var/cache/apt/srcpkgcache.bin - RemoveCaches (13: Permission denied)
109 | $ sudo !!
110 | sudo apt update
111 | [sudo] password for ustc:
112 | Hit:1 http://mirrors.ustc.edu.cn/ubuntu bionic InRelease
113 | (以下内容省略)
114 | ```
115 |
116 | 其实,在 Shell 中,`!!` 即代表上一条命令,可以和其他的命令结合使用。
117 |
118 | 那么,如何以 `root` 之外的用户的身份执行命令呢?加上 `-u 用户名` 的参数即可。
119 |
120 | ```console
121 | $ sudo -u nobody id
122 | uid=65534(nobody) gid=65534(nogroup) groups=65534(nogroup)
123 | ```
124 |
125 | 这里,我们就用 `nobody` 这个用户的身份,执行了 `id`,得到了 `nobody` 的 UID 等信息。
126 |
127 | ??? example "修改 `sudo` 配置的例子:无密码执行 `sudo` (\*)"
128 |
129 | `sudo` 的配置存储在 `/etc/sudoers` 文件中,仅 `root` 用户有权查看和修改。**不要直接修改此文件:对这个文件的任何修改,都应该使用 `visudo` 这个命令完成**。
130 |
131 | 默认的 Ubuntu 配置中,安装时创建的用户在 `sudo` 用户组(下文会提到这个概念)中。在 `sudoers` 文件中,它的配置像这样:
132 |
133 | ```
134 | # Allow members of group sudo to execute any command
135 | %sudo ALL=(ALL:ALL) ALL
136 | ```
137 |
138 | 将配置行修改成以下即可。注意,`%sudo` 的后面是一个制表符(TAB),不是一系列空格。
139 |
140 | ```
141 | %sudo ALL=(ALL:ALL) NOPASSWD:ALL
142 | ```
143 |
144 | `sudoers` 的配置比较复杂。可以使用 `man sudoers`,或在网络上搜索资料以得到更详细的说明。
145 |
146 | #### `su` {#su}
147 |
148 | `su` 命令用于直接切换用户,格式是 `su 用户名`。如果没有用户名这个参数,则切换到 `root` 用户。
149 |
150 | 在读完上面这句话之后,你可能会尝试切换到 `root`,但是却失败了:
151 |
152 | ```console
153 | $ su
154 | Password:
155 | (密码?什么密码?输我自己的密码试试?)
156 | su: Authentication failure
157 | $
158 | ```
159 |
160 | 这是因为,如 Ubuntu 等 Linux 发行版默认禁止了 `root` 用户的密码登录,只允许通过 `sudo` 提高权限。但是,我们可以用 `sudo` 运行 `su`,来得到一个为 `root` 用户权限的 shell。
161 |
162 | ```console
163 | $ sudo su
164 | Password:
165 | (没错,是我自己的密码)
166 | # id
167 | uid=0(root) gid=0(root) groups=0(root)
168 | # exit
169 | $
170 | ```
171 |
172 | `sudo su`, `sudo su -` 等命令有一些细微的区别,可以阅读本章的补充材料。另外,也可以使用 `sudo -i`(与 `sudo su -` 等价),获得一个 `root` 权限 shell。
173 |
174 | ### 用户组简介 {#intro-user-group}
175 |
176 | 用户组是用户的集合。通过用户组机制,可以为一批用户设置权限。可以使用 `groups` 命令,查看自己所属的用户组。
177 |
178 | ```console
179 | $ groups
180 | ustc adm cdrom sudo dip plugdev lxd
181 | ```
182 |
183 | 可以看到,用户 `ustc` 从属于多个用户组,包括一个与其名字相同的用户组。一般在用户创建的时候,都会创建与它名字相同的用户组。
184 |
185 | 对于普通用户来说,用户组机制会在配置部分软件时使用到。如在使用第八章的 Docker 时,可以把自己加入 `docker` 用户组,从而不需要使用 `root` 权限,也可以访问它的接口。
186 |
187 | 同样,用户组和用户一样,也有编号:GID (Group ID)。
188 |
189 | ### 命令行的用户配置操作 {#user-configuration-command-line}
190 |
191 | #### 修改密码:`passwd` {#passwd}
192 |
193 | 可以使用此命令修改用户密码,格式为 `passwd 用户名`。如果没有输入用户名,则修改自己的密码。
194 |
195 | #### (\*) 简单的用户配置:`adduser` {#adduser}
196 |
197 | !!! warning "`adduser` 是 Debian 及其衍生发行版的专属命令"
198 |
199 | 如果你正在使用其他发行版(如 Fedora, CentOS 等),或者有更多的需求,则需要使用 `usermod`, `useradd`, `groupadd` 等替代。具体信息详询各自的 `man` 文档。
200 |
201 | `adduser` 是 Debian 及其衍生发行版中附带的一个方便的用户管理脚本。它可以用来向系统添加用户、添加组,以及将用户加入组。输入:
202 |
203 | ```console
204 | $ sudo adduser 用户名
205 | ```
206 |
207 | 即可添加此用户。而输入
208 |
209 | ```console
210 | $ sudo adduser --group 组名
211 | ```
212 |
213 | 即可添加此用户组。将用户加入指定用户组也非常简单:
214 |
215 | ```console
216 | $ sudo adduser 用户名 组名
217 | ```
218 |
219 | !!! example "添加用户至 sudo 用户组"
220 |
221 | 在通过 `adduser` 创建了新的用户后,直接使用 `sudo` 以 `root` 身份运行程序可能会得到:
222 |
223 | ```text
224 | $ sudo apt update
225 | [sudo] password for ustc:
226 | ustc is not in the sudoers file. This incident will be reported.
227 | ```
228 |
229 | 除了可以通过 `visudo` 命令编辑 `sudoers` 文件外,还可以直接通过将新的用户加入到 `sudo` 用户组,以能够使用 `sudo` 命令。
230 |
231 | ```console
232 | $ sudo adduser ustc sudo
233 | ```
234 |
235 | 再次切换到新的用户即可看到使用 sudo 的提示:
236 |
237 | ```text
238 | To run a command as administrator (user "root"), use "sudo ".
239 | See "man sudo_root" for details.
240 | ```
241 |
242 | ## 文件权限 {#file-permission}
243 |
244 | 在 Linux 中,每个文件和目录都有自己的权限。可以使用 `ls -l` 查看当前目录中文件的详细信息。
245 |
246 | ```console
247 | $ ls -l
248 | total 8
249 | -rwxrw-r-- 1 ustc ustc 40 Feb 3 22:37 a_file
250 | drwxrwxr-x 2 ustc ustc 4096 Feb 3 22:38 a_folder
251 | ```
252 |
253 | 第一列的字符串从左到右意义分别是:文件类型(一位)、文件所属用户的权限(三位)、文件所属用户组的权限(三位)、其他人的权限(三位)。对于每个权限,第一位 `r` 代表读取 (**R**ead),第二位 `w` 代表写入 (**W**rite),第三位 `x` 代表执行 (E**x**ecute),`-` 代表没有对应的权限。
254 |
255 | 第三、四列为文件所属用户和用户组。
256 |
257 | 例如,上面的文件 `a_file` 为普通文件 (`-`),所属用户权限为 `rwx`,所属用户组权限为 `rw-`,其他人的权限为 `r--`,文件所属用户和用户组均为 `ustc`。
258 |
259 | ??? tip "执行权限意味着什么?"
260 |
261 | 读取和写入权限是很容易理解的。但是执行权限是什么意思?对于一个文件来说,拥有执行权限,它就可以被操作系统作为程序代码执行。如果某个程序文件没有执行权限,你仍然可以查看这个程序文件本身,修改它的内容,但是无法执行它。
262 |
263 | 而对于目录来说,拥有执行权限,你就可以访问这个目录下的文件的内容。以下是一个例子:
264 |
265 | ```console
266 | $ ls -l
267 | total 8
268 | -rwxrw-r-- 1 ustc ustc 40 Feb 3 22:37 a_file
269 | drw-rw-r-- 2 ustc ustc 4096 Feb 3 22:38 a_folder
270 | $ (与上面不同,我们去掉了 a_folder 的执行权限)
271 | $ cd a_folder
272 | -bash: cd: a_folder/: Permission denied
273 | $ (失败了,这说明,如果没有执行权限,我们无法进入这个目录)
274 | $ ls a_folder
275 | ls: cannot access 'a_folder/test': Permission denied
276 | test
277 | $ (列出了这个目录中的文件列表,但是因为没有执行权限,我们没有办法访问到里面的文件 test)
278 | $ cat a_folder/test
279 | cat: a_folder/test: Permission denied
280 | $ cp a_folder/test test
281 | cp: cannot stat 'a_folder/test': Permission denied
282 | $ mv a_folder/test a_folder/test2
283 | mv: failed to access 'a_folder/test2': Permission denied
284 | $ touch a_folder/test2
285 | touch: cannot touch 'a_folder/test2': Permission denied
286 | $ rm a_folder/test
287 | rm: cannot remove 'a_folder/test': Permission denied
288 | $ (可以看到,即使我们有写入权限,在此目录中进行添加、删除、重命名的操作仍然是不行的)
289 | ```
290 |
291 | 为了更好地理解目录权限的含义,可以把目录视为一个「文件」来看待,这个文件包含了目录中下一层的文件列表——「读取」对应读取文件列表的权限,「写入」对应修改文件列表(添加、删除、重命名文件)的权限,「执行」对应实际去访问列表中文件、以及使用 `cd` 切换当前目录到此目录的权限。特别地,父目录(`..`)也在这个列表中,因此将目录移动到其他目录下时,不仅需要该目录所在目录和目标目录的写权限,还需要该目录本身的写权限。例如,将目录 `/opt/example/1/` 移动为 `/opt/1/` 时,用户不仅需要对 `/opt/example/` 和 `/opt/` 目录有写入权限,被移动的 `1` 目录也需要有写入权限。
292 |
293 | 有关文件与目录权限的完整表格,可以查看 Arch Wiki 的 [File permissions and attributes](https://wiki.archlinux.org/index.php/File_permissions_and_attributes#Viewing_permissions) 一页。
294 |
295 | 可以使用 `chmod` (**ch**ange file **mod**e bits) 修改权限,`chown` (**ch**ange file **own**er) 修改文件所有者。具体的使用方法,请查阅相关的文档,这里不再列出。
296 |
297 | !!! example "例子:如何执行从网络上下载的程序或脚本"
298 |
299 | 有时候,我们会从网上下载一些二进制的程序,或者根据网络的教程编写脚本程序,但当你想执行的时候却发现:
300 |
301 | ```console
302 | $ ./program
303 | -bash: ./program: Permission denied
304 | ```
305 |
306 | 大多数情况下,这说明这个文件缺少执行 (`x`) 权限。可以使用 `chmod +x` 命令添加执行权限。
307 |
308 | ```console
309 | $ chmod +x program
310 | $ ./program
311 | (可以执行了)
312 | ```
313 |
314 | ## 文件系统层次结构 {#fhs}
315 |
316 | 相信到现在你应该已经发现了:Linux 下文件系统的结构和 Windows 的很不一样。在 Windows 中,分区以盘符的形式来标识(如「C 盘」、「D 盘」),各个分区的分界线是很明确的。在系统所在的分区(一般为 C 盘)中,存储着程序文件 (`Program Files`),系统运行需要的文件 (`Windows`),用户文件 (`Users`) 等。这种组织形式源于 DOS 和早期的 Windows,并一直传承下来。
317 |
318 | 而 UNIX 系列采用了一种不一样的思路组织文件:整个系统的文件都从 `/`(根目录)开始,像一棵树一样,类似于下图。
319 |
320 | ```mermaid
321 | graph LR
322 | / --> bin
323 | / --> boot
324 | / --> dev
325 | / --> etc
326 | / --> home
327 | / --> mnt
328 | / --> opt
329 | / --> proc
330 | / --> root
331 | / --> usr
332 | / --> var
333 | / --> tmp
334 |
335 | bin --> ls
336 | bin --> cp
337 |
338 | dev --> zero
339 | dev --> null
340 |
341 | home --> zhangsan
342 | home --> lisi
343 |
344 | zhangsan --> code
345 | zhangsan --> tools
346 |
347 | lisi --> music
348 | lisi --> docs
349 |
350 | mnt --> windows_disk
351 | subgraph mount
352 | windows_disk --> Windows
353 | windows_disk --> Users
354 | end
355 |
356 | usr --> usrbin[bin]
357 | ```
358 |
359 | 其他的分区以挂载 (mount) 的形式「挂」在了这棵树上,如图中的 `/mnt/windows_disk/`。
360 |
361 | 那么在根目录下的这些目录各自有什么含义呢?这就由文件系统层次结构标准 (FHS, Filesystem Hierarchy Standard) 来定义了。这个标准定义了 Linux 发行版的标准目录结构。大部分的 Linux 发行版遵循此标准,或由此标准做了细小的调整。以下进行一个简要的介绍。也可以在[官网](https://refspecs.linuxfoundation.org/FHS_3.0/fhs/index.html)查看标准的具体内容。
362 |
363 | 当然,实际情况不一定会和以下介绍的内容完全一致。可以使用 `man hier` 和 `man file-hierarchy` 查看你的系统中关于文件系统层次结构的文档。
364 |
365 | `/bin`
366 | : 存储必须的程序文件,对所有用户都可用。
367 |
368 | `/boot`
369 | : 存储在启动系统时需要的文件。
370 |
371 | `/dev`
372 | : 存储设备文件。
373 |
374 | !!! tip "什么是设备文件?"
375 |
376 | 在 Linux 的哲学中,存在着「一切皆文件」这样的思想。设备文件就是计算机设备抽象成文件的形式,程序和用户可以以读写普通文件的方式向这些文件输入内容,或者从文件中获取内容。系统驱动程序会相应处理用户对对应设备文件的输入和输出。
377 |
378 | 有一些常用的设备文件如:
379 |
380 | - `/dev/null`:总是返回空(EOF)数据。
381 | - `/dev/zero`:总是返回零数据。
382 | - `/dev/urandom`:输出随机数据。
383 |
384 | 配合[第六章中提到的重定向功能](../Ch06/index.md#redirection),这些设备文件可以帮助我们做到丢弃程序输出等操作。
385 |
386 | `/etc`
387 |
388 | : 存储系统和程序的配置文件。
389 |
390 | !!! tip "注册表与配置文件"
391 |
392 | Windows 系统与其上很多程序都使用注册表,而非文件的形式存储配置信息。注册表是一个数据库,拥有数据库的优点(如原子性),集中地管理配置项。而 Linux 下的程序更喜欢将配置以文件的形式存储,保持配置简单,便于用户编辑与备份。
393 |
394 | 当然,这不是绝对的。现在 Windows 下的 .NET 程序更偏向于将配置存储在 XML 文件中,而 Linux 下的 GNOME 桌面环境也采取了类似于注册表的形式: `GConf`,存储自己的配置信息。
395 |
396 | `/home`
397 |
398 | : 用户的家目录。存储用户自己的信息。
399 |
400 | `/lib`
401 |
402 | : 存放系统运行必须的程序库文件。
403 |
404 | `/media` 和 `/mnt`
405 |
406 | : 这两个目录都用于挂载其他的文件系统。`/media` 用于可移除的文件系统(如光盘),而 `/mnt` 用于临时使用。
407 |
408 | `/opt`
409 |
410 | : 存放额外的程序包。一般将一些大型的、商业的应用程序放置在这个目录。
411 |
412 | `/root`
413 |
414 | : `root` 用户的家目录。
415 |
416 | `/run`
417 |
418 | : 系统运行时的数据。在每次启动时,里面的数据都会被删除。
419 |
420 | `/sbin`
421 |
422 | : 存储用于系统管理,以及仅允许 `root` 用户使用的程序。如 `fsck`(文件系统修复程序)、`reboot`(重启系统)、`useradd`(添加用户)等。
423 |
424 | `/srv`
425 |
426 | : 存储网络服务的数据。
427 |
428 | `/tmp`
429 | : 临时目录,所有用户都可使用。
430 |
431 | `/usr`
432 | : 大多数软件都会安装在此处。其下有一些目录与 `/` 下的结构相似,如:
433 |
434 | - `/usr/bin`
435 | - `/usr/lib`
436 | - `/usr/sbin`
437 |
438 | 此外,还有一些目录:
439 |
440 | - `/usr/include`: 存储系统通用的 C 头文件。当然,里面会有你非常熟悉的头文件,如 `stdio.h`。
441 | - `/usr/local`: 存储系统管理员自己安装的程序,这些文件不受系统的软件管理机制(如 `apt`)控制。`/usr/local` 里面的层次结构和 `/usr` 相似。
442 | - `/usr/share`: 存储程序的数据文件(如 `man` 文档、GUI 程序使用的图片等)。
443 |
444 | !!! tip "`usrmerge`"
445 |
446 | 近年来,部分发行版选择将 `/usr/bin`、`/usr/sbin`、`/usr/lib` 与根目录下的 `/bin`、`/sbin`、`/lib` 合并,根目录下的对应目录软链接到 `/usr` 下的目录,以简化文件结构。
447 |
448 | Ubuntu 与 Debian 中可以安装 `usrmerge` 软件包来进行转换。
449 |
450 | `/var`
451 | : 存储会发生变化的程序相关文件。例如下面的目录:
452 |
453 | - `/var/log`:存储程序的日志文件。
454 | - `/var/lib`:存储程序自身的状态信息(如 lock file)。这个目录和 `library` 的关系并不大。
455 | - `/var/run`:存储程序运行时的数据(部分发行版会将该目录符号链接到 `/run` 目录)。
456 | - `/var/spool`:存储「等待进一步处理」的程序数据。
457 |
458 | ## 思考题 {#questions}
459 |
460 | !!! question "nobody 用户"
461 |
462 | 关于 `nobody` 用户,网络上有一种说法称,所有网络服务(如 Web 服务器)都应该以此用户身份运行。从安全性的角度反驳此观点。
463 |
464 | !!! question "系统用户的默认 Shell"
465 |
466 | 在 `/etc/passwd` 中最后一列是用户的默认 Shell(如果你没有修改过的话,那么一般是 `/bin/bash`)。为什么会有很多用户的默认 Shell 是 `/usr/sbin/nologin` 或者 `/bin/false`?这样做有什么意义?
467 |
468 | !!! question "启用 root 用户"
469 |
470 | 在 Ubuntu 等 Linux 发行版中,`root` 用户是默认禁用(无法直接使用密码登录)的。如何启用此用户?
471 |
472 | !!! question "文件的可执行权限"
473 |
474 | 如果向一个 MP3 音频文件添加「可执行」(`x`) 属性,那么这个文件就可以被执行吗?为什么?
475 |
476 | !!! question "`sudo cd`?"
477 |
478 | 当需要浏览仅 `root` 用户可查看的目录时,很多人的第一反应是 `sudo cd xxx`,但最终失败了。尝试解释这样做不可行的原因。
479 |
480 | !!! question "Debian 与 Ubuntu 的区别之一:普通用户运行 `useradd` 等命令"
481 |
482 | 提示:可以在按照[第八章](../Ch08/index.md)配置 Docker 后使用如下命令体验 Debian:
483 |
484 | ```console
485 | $ sudo docker run -it --rm debian:bookworm
486 | ```
487 |
488 | 在进入容器后,使用 `useradd`(`adduser` 也可以)创建一个新用户并进入:
489 |
490 | ```console
491 | # useradd -m test
492 | # su - test
493 | $ useradd
494 | -sh: 1: useradd: not found
495 | ```
496 |
497 | 而相同的操作在 Ubuntu 容器(`ubuntu:noble`)中可以找到 `useradd` 这个命令:
498 |
499 | ```console
500 | $ useradd
501 | Usage: useradd [options] LOGIN
502 | useradd -D
503 | useradd -D [options]
504 | ```
505 |
506 | 虽然说 `useradd` 这种程序只能 root 运行,但是以上差异是为什么呢?
507 |
508 | ## 引用来源 {#references .no-underline}
509 |
510 | - [维基百科上的 Passwd 词条(英语)](https://en.wikipedia.org/wiki/Passwd)
511 | - [Simple explanation of `sudoers` file](https://askubuntu.com/questions/118204/5958455) - Ask Ubuntu
512 | - [Sudoers - Community Help Wiki](https://help.ubuntu.com/community/Sudoers) - Ubuntu Documentation
513 |
514 | [^1]: 这里的哈希,指经过了[密码哈希函数](https://zh.wikipedia.org/wiki/%E5%AF%86%E7%A2%BC%E9%9B%9C%E6%B9%8A%E5%87%BD%E6%95%B8) ([Cryptographic hash function](https://en.wikipedia.org/wiki/Cryptographic_hash_function)) 的处理。密码哈希函数是一种特殊的单向函数,将任意大小的数据映射到一串长度固定的字符串,并且拥有一些优良的性质(如难以找到两个不同的数据,使得映射后的字符串相同),使其破解难度加大。
515 | [^3]:
516 | [^2]: 然而,对于 Linux 内核来说,系统用户和真实的用户其实没有区别,除了 UID = 0 的用户 (root) 以外。「系统用户」是一个约定俗成而产生的概念。
517 |
--------------------------------------------------------------------------------
/docs/Ch05/solution.md:
--------------------------------------------------------------------------------
1 | ---
2 | icon: material/tooltip-question
3 | ---
4 |
5 | # 思考题解答 {#solution}
6 |
7 | ## nobody 用户
8 |
9 | !!! tip "提示"
10 |
11 | 我们提到,为什么要对网络服务创建用户?
12 |
13 | ??? info "解答"
14 |
15 | 将**所有**的网络服务放在同一个用户来执行显然是不明智的选择:如果有一个服务被攻击,那么其他同用户的服务也同时会被攻击者控制。
16 |
17 | ## 系统用户的默认 Shell
18 |
19 | !!! tip "提示"
20 |
21 | 尝试执行一下系统用户的 Shell,可以看到什么?
22 |
23 | ??? info "解答"
24 |
25 | ```console
26 | $ /usr/sbin/nologin
27 | This account is currently not available.
28 | $ /bin/false
29 | $ echo $?
30 | 1
31 | ```
32 |
33 | 以它们作为系统用户的默认 Shell,也就实现了「无法登录」的效果,提高了系统用户的安全性。以下是一个能够说明其「安全性」的例子:
34 |
35 | 假设某个(运行在某系统用户上的)服务被攻击者发现可以任意以某系统用户的权限写入文件。假设 SSH 开启,攻击者可以把自己的公钥写入 `~/.ssh/authorized_keys`,然后使用自己的私钥登录此用户,得到 shell。
36 |
37 | 但是,如果系统用户的 shell 设置为了 `/usr/sbin/nologin` 或者 `/bin/false`,那么即使发生了这种情况,攻击者也无法使用 SSH 连接到 shell,或者执行其他命令。这就提高了系统的安全性。
38 |
39 | ## 启用 root 用户
40 |
41 | ??? info "解答"
42 |
43 | `/etc/shadow` 第二列(记录密码哈希的那一列)的 `!` 或 `*` 表明这个用户不允许使用密码登录。那么我们再用 `passwd` 设置个密码就可以了。
44 |
45 | 如果要重新禁止使用密码登录 `root` 用户的话,`passwd -l root` 即可。
46 |
47 | ## 文件的可执行权限
48 |
49 | ??? info "解答"
50 |
51 | 它**可以**被执行,但是执行显然会出错。Linux 下的可执行文件一般有两类:第一类是 ELF 二进制文件,它的文件开头的十六进制是 `7F 45 4C 46`(这一串有特征的字符被称为「文件标识 (Signature)」,方便程序判断文件的格式信息);第二类是脚本,脚本的开头一般有 `#!` (Shebang) 标明文件由什么程序解析。
52 |
53 | 显然,Linux 不会认为一个 MP3 音频文件是 ELF(文件标识不一致),也不是脚本,那么直接执行,就会报出 `cannot execute binary file: Exec format error` 的错误。
54 |
55 | ## `sudo cd`?
56 |
57 | !!! tip "提示"
58 |
59 | `cd` 是一个「程序」吗?
60 |
61 | ??? info "解答"
62 |
63 | 如果你真的去执行 `sudo cd`,那么会看到:
64 |
65 | ```console
66 | $ sudo cd a
67 | sudo: cd: command not found
68 | ```
69 |
70 | 这是因为,`cd` 是 shell 的**内建命令**,而不是某个具体的程序,而 `sudo` 的功能,是以其他用户 (一般是 root) 的身份执行程序。
71 |
72 | 那么 `cd` 可以实现成(外置的)程序吗?答案是不能:因为切换工作目录的系统调用 (`chdir()`) 只能切换当前的程序的工作目录。如果实现成了外置的程序,那么切换完退出之后,shell 的工作目录仍然不会变化。
73 |
74 | ## Debian 与 Ubuntu 的区别之一:普通用户运行 `useradd` 等命令
75 |
76 | ??? info "解答"
77 |
78 | 这里可能不止权限不足的问题。这是因为,`useradd` 存在于 `/sbin` 下,而在 Debian 中,这个目录并不在普通用户登录后默认的 `PATH` 环境变量中(但 Ubuntu 下则不一样:`/sbin` 也在普通用户的 `PATH` 环境变量中)。也就是说,Shell 并不会去 `/sbin` 中查找 `useradd`,自然就会提示 `command not found`。如果改成完整路径(`/sbin/useradd`)就可以了。
79 |
--------------------------------------------------------------------------------
/docs/Ch05/supplement.md:
--------------------------------------------------------------------------------
1 | ---
2 | icon: material/puzzle
3 | ---
4 |
5 | # 拓展阅读 {#supplement}
6 |
7 | ## 文件系统的特殊权限位 {#special-permission-bit}
8 |
9 | 有一个有趣的问题:众所周知(只要你看过了本章的主要内容),存储密码的 `/etc/shadow` 只有 `root` 用户可以查看和修改。但是,我作为一个普通用户,可以使用 `passwd` 修改自己的密码。而要修改密码,就必须修改 `/etc/shadow`,而我执行的 `passwd` 程序(应该)只拥有我的权限(否则用户权限就没有任何意义了)。难道 `passwd` 有什么魔法可以去动 `/etc/shadow` 吗?
10 |
11 | 是的。这样的「魔法」,是由文件系统特殊权限位 `setuid` 赋予的。有三个特殊权限位:setuid, setgid 和 sticky。
12 |
13 | - `setuid`: 以文件所属的用户的身份 (UID) 执行此程序。
14 | - `setgid`: 对文件来说,以文件所属的用户组的身份 (GID) 执行此程序;对目录来说,在这个目录下创建的文件的用户组都与此目录本身的用户组一致,而不是创建者的用户组。
15 | - `sticky` (restricted deletion flag): 目录中的所有文件只能由文件所有者(除 `root` 以外)删除或者移动。一个典型的例子是临时文件夹 `/tmp`,在此文件夹中你可以创建、修改、重命名、移动、删除自己的文件,但是动不了别人的文件。
16 |
17 | ??? tip "setuid 和 sticky 位的历史"
18 |
19 | 聪明的你可能已经注意到了:setuid 和 setgid 对可执行程序具有相同的语义,但 setuid 对目录没有作用。事实上大部分的 UNIX 系统和所有的 Linux 都从未对“设有 setuid 的目录”赋予过任何特别含义,但仍然有少部分系统可以配置为对目录中的 setuid 位采取和 setgid 位类似的语义,即此目录中新建的文件所有者与此目录的所有者一致,而不是为文件的创建者所有。这“少部分系统”中的一个例子是 FreeBSD,但仅当文件系统在挂载时配置了 `suiddir` 参数([FreeBSD](https://www.freebsd.org/cgi/man.cgi?query=mount&sektion=8&apropos=0&manpath=FreeBSD+6.1-RELEASE))。
20 |
21 | 在很早的时候(1974 年 Unix 第五版),sticky bit 是为可执行文件设计的,它告诉操作系统在程序运行结束后将程序的代码段(text 段)保留在内存或交换区中,以便下一次更快地启动这个程序,因此这个位被称作 sticky(粘滞位)。由于现代计算机存储容量的增加和操作系统缓存功能的完善,已经没有系统再将 sticky bit 解释为这个最初的含义了。在 Linux 系统中,文件上的 sticky bit 从未有过任何功能,仅有目录的 sticky bit 功能如上所述。
22 |
23 | 我们可以看一下,`/usr/bin/passwd` 的文件权限设置:
24 |
25 | ```console
26 | $ ls -l /usr/bin/passwd
27 | -rwsr-xr-x 1 root root 67992 Aug 29 2019 /usr/bin/passwd
28 | ```
29 |
30 | 可以看到,本来是执行权限位的地方变成了 `s`。这代表此文件有 `setuid` 特殊权限位。在你执行 `passwd` 的时候,它的实际权限和 `root` 一样,只是它知道,执行它的人是你(而非 `root`),所以只提供修改你自己的密码的功能。
31 |
32 | 同样,`su` 和 `sudo` 也有 `setuid` 权限位:
33 |
34 | ```console
35 | $ ls -l /usr/bin/su
36 | -rwsr-xr-x 1 root root 67816 Jan 9 02:59 /usr/bin/su
37 | $ ls -l /usr/bin/sudo
38 | -rwsr-xr-x 1 root root 161448 Feb 1 01:07 /usr/bin/sudo
39 | ```
40 |
41 | 所以它们可以帮你切换用户、提升权限。
42 |
43 | 拥有 `setuid` 位、所有者为 `root` 的程序是非常危险的,因为稍不谨慎,它们的漏洞就会直接让普通的无权限用户获得 `root` 权限的大门,这在服务器上是极其致命的。`sudo` 曾经爆出一个安全漏洞(CVE-2019-18634),对于 1.8.31 之前的版本,`sudo` 在开启了 `pwfeedback` 选项(将输入的密码显示为 \* 号,而非不显示)之后,有一个缓冲区溢出漏洞可以被利用来以 `root` 的身份执行任意命令;而在 2022 年初,另一个用于验证用户身份、提升权限的工具 `pkexec` 也被爆出安全漏洞(CVE-2021-4034):由于编写 C 语言程序时未考虑到 `main()` 函数参数 `argc` 可以为 0 的情况,攻击者可以编写程序引发其逻辑问题,从而提升自己的权限。
44 |
45 | 当然,Linux 在发展中也在尽可能减少 `setuid` 程序的使用。例如 `ping` 程序因为需要创建只能由 `root` 用户创建的原始 (raw) 的网络套接字 (socket),在以往也是一个 `setuid` 程序。但是随着 "Capabilities" 的概念引入 Linux 内核,`ping` 不再需要 `setuid`,只需要为它设置创建原始网络套接字的 capability 即可,提高了系统的安全性。
46 |
47 | ## 实际用户与有效用户 {#uid-and-euid}
48 |
49 | 于是,我们就有了另一个问题:如果 `passwd` 在执行的时候的权限是 `root` 的话,那它是怎么知道是我(而不是 `root`)执行它的呢?
50 |
51 | 在 Linux 中,有两个系统调用可以获取当前进程归属的 UID:`getuid()` 和 `geteuid()`。前者对应的是「实际用户」(Real user),是实际运行(拥有)这个进程的用户,后者对应的是「有效用户」(Effective user),对应进程拥有的权限。在运行 `passwd` 的时候,有效用户是 `root`,所以可以修改 `/etc/shadow`;而实际用户是你,所以它不会让你修改别人的密码。
52 |
53 | 对用户组来说,也有实际用户组 (GID) 和有效用户组 (EGID) 的区别。
54 |
55 | ## 「登录 Shell」(Login shell) 与「非登录 Shell」(Non-login shell) {#login-and-non-login-shell}
56 |
57 | 在前文中我们提到,`su` 和 `su -` 是有区别的。你也可能在学习 Linux 的时候会好奇:为什么我按下 Ctrl + Alt + F\[1-7\] 的时候出现的 TTY 会问我要用户名和密码,但是在桌面环境中点「终端」,不需要再输入用户名和密码,就可以操作。
58 |
59 | 这就涉及到「登录 Shell」和「非登录 Shell」的差别了。「登录 Shell」是属于你的当前会话操作中的第一个进程,一般是在你输入用户名和密码之后打开的 Shell。常见的场景有:
60 |
61 | - `su -` 之后的 Shell。
62 | - SSH 登录机器后的 Shell
63 | - Ctrl + Alt + F\[1-7\] 之后 TTY 中的 Shell
64 |
65 | 而「非登录 Shell」的常见场景:
66 |
67 | - `su` 打开的是「非登录 Shell」
68 | - 在桌面环境中打开的终端(模拟器),启动的也是「非登录 Shell」
69 |
70 | 一般地,「登录 Shell」会额外加载 `profile` 文件(文件名根据你使用的 Shell 的不同而有区别),且它的 `argv[0][0] == '-'`(相信你已经学过 C 语言了)。可以用以下方法验证:
71 |
72 | ```console
73 | $ echo $0 # 查看当前 Shell 的 argv[0] 的值
74 | -bash
75 | $ # 是 Login shell
76 | $ sudo su # 进入 root
77 | # echo $0
78 | bash
79 | # # 是 Non-login shell
80 | ```
81 |
--------------------------------------------------------------------------------
/docs/Ch06/images/pipe.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch06/images/pipe.png
--------------------------------------------------------------------------------
/docs/Ch06/solution.md:
--------------------------------------------------------------------------------
1 | ---
2 | icon: material/tooltip-question
3 | ---
4 |
5 | # 思考题解答 {#solution}
6 |
7 | ## I/O 重定向的小细节
8 |
9 | ??? info "解答"
10 |
11 | ```console
12 | $ wc -l file
13 | 427 file
14 | $ wc -l < file
15 | 427
16 | $ echo < file
17 |
18 | $
19 | ```
20 |
21 | 可以看到,两种情况下 `wc` 都正确输出了文件的行数,因为 `wc` 既可以从标准输入读取内容,也可以从文件读取内容。当然,从标准输入读取内容的时候,`wc` 不知道文件名,所以没有显示出来。
22 |
23 | 而 `echo` 只输出了空行,因为 `echo` 不从标准输入读取内容(它的输入从参数读取)。
24 |
25 | ## 设定 HTTP 请求头
26 |
27 | ??? info "解答"
28 |
29 | `curl` 的 `-H` 参数可以设定 HTTP 的请求头,如 `curl -v -H 'User-Agent: Test' http://www.baidu.com`.
30 |
31 | 而对于 `wget`,对应的参数则是 `--header`.
32 |
33 | ## 关于断点续传
34 |
35 | !!! tip "提示"
36 |
37 | 可能需要简单学习 HTTP 协议的大体特性。另外,如果你想要实现这样一个特性,你会希望服务器支持什么功能呢?
38 |
39 | ??? info "解答"
40 |
41 | 关键词是:Range 和 Content-Range.
42 |
43 | 客户端如果需要从断开的地方重新开始,那么它需要向服务器指定需要下载的文件的范围,这个工作由 HTTP 请求头 `Range` 完成。而服务器在响应这样的请求时,响应头就包含 `Content-Range`,标志实际包含的文件内容的范围。
44 |
45 | 当然,不是所有服务器都支持这个特性,所以有的时候你可能会发现,「断点续传」会失败。
46 |
47 | ## `/usr/bin/env`
48 |
49 | !!! tip "提示"
50 |
51 | 一个常见的例子是,`#!/usr/bin/env python3`.
52 |
53 | ??? info "解答"
54 |
55 | `#!` (Shebang) 指定了脚本文件由哪一个程序去解释,但是可以发现,这个程序必须是绝对路径。这导致了在跨平台的时候,这样的脚本容易出现问题:比如说可能在不同的环境中,Python 3 可能在 `/usr/bin/python3`, 可能在 `/usr/local/bin/python3`,甚至可能在其他的地方(比如说,如果你安装了 pyenv,那么就在 `~/.pyenv/shims/python3`)。
56 |
57 | `/usr/bin/env` 的功能是,在指定的环境变量中执行命令,它的参数可以不是程序的绝对路径。这个特性就让它变成了 Shebang 的常客:只要 `env` 在 `/usr/bin` 下面(一般都是这样子的),并且后面的命令可以执行,那么就没有问题了。
58 |
59 | ## Shell 脚本编写练习 #1
60 |
61 | !!! tip "提示"
62 |
63 | 可以使用循环和通配符来完成。另外也需要简单掌握 ffmpeg 的使用(不需要对编码参数等有所了解)。
64 |
65 | ??? info "解答"
66 |
67 | 参考脚本:
68 |
69 | ```shell
70 | #!/bin/bash
71 |
72 | for i in *.mp4
73 | do
74 | ffmpeg -i "$i" "${i%.mp4}.mp3"
75 | done
76 | ```
77 |
78 | 这里 `"${i%.mp4}.mp3"` 实现的是将文件扩展名中的 `.mp4`(即最后被匹配到的 `.mp4`)抹去,并在之后的文件名后面加上 `.mp3`。这个特性被称为[参数操作](http://mywiki.wooledge.org/BashSheet#Parameter_Operations)。
79 |
80 | `"${i/.mp4/.mp3}"` 的效果是类似的,但是直接将 `.mp4` 替换为 `.mp3` 的问题是,如果有视频文件名为 `xxx.mp4.xxx.mp4` 的话,非扩展名部分的 `mp4` 也会变为 `mp3`。
81 |
82 | ## Shell 脚本编写练习 #2
83 |
84 | !!! tip "提示"
85 |
86 | 简单的数学计算,分支指令,与 `&&`。
87 |
88 | ??? info "解答"
89 |
90 | 参考脚本:
91 |
92 | ```shell
93 | #!/bin/bash
94 |
95 | if [ -z "$1" ]
96 | then
97 | echo "Usage" "$0" "NUMBER"
98 | else
99 | x="$1"
100 | y=$((x*x))
101 | ./a "$x" && ./b "$y"
102 | fi
103 | ```
104 |
105 | ## Shell 脚本编写练习 #3
106 |
107 | !!! tip "提示"
108 |
109 | 这里,需要解析 HTML 中所有的(`a` 标签的)`href` 属性,挑选出所有后缀为 `.pdf` 的文件,并调用 `wget` 下载。
110 |
111 | ??? info "解答"
112 |
113 | 参考脚本:
114 |
115 | ```shell
116 | #!/bin/bash
117 |
118 | url="http://staff.ustc.edu.cn/~bjye/em/student/2019S/2019S.htm"
119 | baseurl="http://staff.ustc.edu.cn/~bjye/em/student/2019S/"
120 | list=$(curl "$url" | grep -Eoi 'href=".+\.pdf' | cut -c 7-)
121 | for i in $list
122 | do
123 | wget "$baseurl$i"
124 | done
125 | ```
126 |
127 | 注意:在不同的环境中,`grep` 等程序有一些细节上的差异。例如,以上的脚本就无法在 macOS 环境中很好地运行(需要安装 GNU grep)。
128 |
129 | 另外,此 Shell 脚本只适用于这个特定的页面,并且没有去重。如果需要更加通用的从 HTML 中提取链接并下载的方案,可能需要使用其他编程语言中更加成熟的 HTML 解析框架。
130 |
--------------------------------------------------------------------------------
/docs/Ch06/supplement.md:
--------------------------------------------------------------------------------
1 | ---
2 | icon: material/puzzle
3 | ---
4 |
5 | # 拓展阅读 {#supplement}
6 |
7 | ## 类 C 语言的 for 循环 {#c-style-for}
8 |
9 | ```shell
10 | for((assignment;condition;next));do
11 | command_1;
12 | command_2;
13 | done;
14 | ```
15 |
16 | 这里的 for 循环与 C 中的相似。我们知道,在 shell 里变量调用需要加 `$`,但是 for 中的 `(())` 中不需要。
17 |
18 | !!! example "范例"
19 |
20 | shell 脚本内容:
21 |
22 | ```shell
23 | #!/bin/bash
24 |
25 | for((i=1;i<=5;i++));do
26 | echo "这是第 $i 次调用";
27 | done;
28 | ```
29 |
30 | 输出结果:
31 |
32 | ```text
33 | 这是第 1 次调用
34 | 这是第 2 次调用
35 | 这是第 3 次调用
36 | 这是第 4 次调用
37 | 这是第 5 次调用
38 | ```
39 |
40 | 与 C 中相似,赋值和下一步执行可以放到代码之前循环语句之中执行,这里要注意一点:如果要在循环体中进行 for 中的 next 操作,记得变量要加 $,不然程序会变成死循环。
41 |
42 | ## Fork 炸弹原理解析 {#fork-bomb}
43 |
44 | !!! Warning "命令含有危险性"
45 |
46 | 以下的命令均包含一定的危险性,请在任何情况下都不要执行,除非你清楚你在做什么。
47 |
48 | 在[第五章](../Ch05/index.md)中,我们介绍过 fork 炸弹。它通过创建大量进程消耗大量的系统资源,从而拖慢系统与正常进程的运行速度,增大响应时间,使得操作系统的正常运作受到较大影响。[^1]
49 |
50 | ### 原理 {#fork-bomb-theory}
51 |
52 | Fork 炸弹有如下的这种形式:
53 |
54 | ```shell
55 | :(){ :|: & };:
56 | ```
57 |
58 | 这是一个函数定义以及对其的调用语句,可以格式化为:
59 |
60 | ```shell
61 | :()
62 | {
63 | :|: &
64 | };
65 | :
66 | ```
67 |
68 | 在 Bash 中,`:`、`.`、`/` 等一些字符也能够被用于函数命名,因此,上面的代码等价于:
69 |
70 | ```shell
71 | func()
72 | {
73 | func | func &
74 | };
75 | func
76 | ```
77 |
78 | fork 炸弹的核心是函数内容:`func | func &`
79 |
80 | - 第一个 func 代表递归执行这个函数。
81 | - | 代表要将第一个函数的数据结果通过管道传输给后一个函数。
82 | - & 代表要在后台执行这一条命令,如果其中一个函数被操作系统回收,其调用产生的子函数并不会被回收。
83 |
84 | 于是运行一次这个函数就会创建两个 func 函数的实例,并不断地反复调用。实例的数量会指数爆炸式地增长,最终耗尽系统的资源。
85 |
86 | ### 防范方法 {#fork-bomb-prevention}
87 |
88 | 一个有效的方式[^1]是通过修改系统配置,限制一个用户能够拥有的进程数量多少。`ulimit -u 30` 可以限制当前用户能够拥有的进程数量为 30。
89 |
90 | ## 引用来源 {#references .no-underline }
91 |
92 | [^1]: [Fork Bomb](https://en.wikipedia.org/wiki/Fork_bomb)
93 |
--------------------------------------------------------------------------------
/docs/Ch07/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | icon: material/xml
3 | ---
4 |
5 | # Linux 上的编程
6 |
7 | !!! success "本文已完稿并通过审阅,是正式版本。"
8 |
9 | !!! abstract "导言"
10 |
11 | 作为一个成熟而实用的系统,我们该如何在 Linux 上进行日常的编程开发呢?
12 | 这一章将解答一下几个问题:
13 |
14 | - Linux 上的 C/C++ 开发
15 | - Linux 上的 Python 开发
16 | - Linux 上编程语言开发的范式与共性
17 |
18 | ## C 语言开发 {#c}
19 |
20 | C 语言是大学编程语言教学中几乎必定讲解的一门编程语言。
21 | 考虑到 Linux 内核即是用 C 语言编写的,在 Linux 上 C 语言拥有近乎系统级的支持。
22 | 在 Linux 上开发 C 语言(以及 C++)是一件非常轻松、方便的事。
23 |
24 | ### 从单文件开始 {#c-single-file}
25 |
26 | 现在假设我们有一份源码文件 main.c,内容如下:
27 |
28 | ```c
29 | // main.c
30 | #include
31 |
32 | int main() {
33 | printf("Hello World!\n");
34 | return 0;
35 | }
36 | ```
37 |
38 | 这是一个简单的 Hello World 程序。我们如何使它变为一份二进制可执行文件呢?
39 |
40 | 在 Windows 或 Mac OS X 这样带 GUI 的系统上,通过安装 IDE,我们可以使用 IDE 中的编译功能来编译出目标。
41 | 实际上,这些带有图形界面的 IDE 的编译往往是封装了各种提供命令行接口的编译器。
42 | 自然,在众多无 GUI 的 Linux 上,我们同样可以调用这些提供命令行接口的编译器进行编译。
43 |
44 | !!! tip "各平台常见编译器"
45 |
46 | Linux 上常用的编译器是 gcc 和 clang。
47 | 其中 gcc 是由 GNU 组织维护的,而 clang 是由 LLVM 组织维护的。
48 |
49 | Windows 上常见的编译器则是 cl.exe,由微软维护。著名的 Visual C++ (MSVC) 即使用了 cl.exe。
50 |
51 | Mac OS X 本身由 BSD 发展而来,也以 gcc 和 clang 为主。
52 | 值得一提的是,Mac OS X 上自带的 gcc 其实是 clang 的别名,在 Terminal 输入 `gcc -v` 即可发现。
53 |
54 | 这里我们使用 gcc 对这个文件进行编译,生成二进制文件:
55 |
56 | ```console
57 | $ gcc main.c -o main
58 | $ ./main
59 | Hello World!
60 | ```
61 |
62 | 这里用 `-o` 指定了输出的二进制文件的文件名 `main`。
63 |
64 | 应当注意到 `gcc main.c -o main` 这条指令没有打印出任何内容。
65 | 这是因为整个编译过程是成功的,gcc 没有需要报告的内容,因此保持沉默。
66 | 这是 Unix 哲学的一部分:_Rule of Silence: When a program has nothing surprising to say, it should say nothing._[^1]
67 |
68 | ### 多文件的状况 {#c-multi-file}
69 |
70 | 只在一个文件中编写代码,对于稍微大的开发都是不够的:
71 | 对于个人维护的小项目尚可,但当你面临的是一个多人开发、模块复杂、功能繁多的大项目时(无论是在公司工程还是在实验室科研中,这都是普遍的情况),
72 | 拆分代码到多个文件才是一个明智(或者说可行)的做法。
73 |
74 | !!! tip "C 语言的多文件实现"
75 |
76 | 我们假设你对于 C 语言的多文件实现有着基本的认知:
77 | 即能够在之前的系统中的 IDE 内完成 C 语言的多文件开发。
78 |
79 | 如果你不会,别急,这里将做一个简单的介绍:
80 |
81 | 假设你拥有一下两个文件:
82 |
83 | ```c
84 | // main.c
85 | #include "print.h"
86 |
87 | int main() {
88 | print();
89 | return 0;
90 | }
91 | ```
92 |
93 | ```c
94 | // print.c
95 | #include "print.h"
96 |
97 | #include
98 |
99 | void print() {
100 | printf("Hello World!\n");
101 | }
102 | ```
103 |
104 | 那为了在 main.c 中调用 `void print()` 这个函数,你需要做以下几件事:
105 |
106 | - 在当前目录下新建一个头文件 print.h;
107 | - 在 print.h 中填入以下内容:
108 |
109 | ```c
110 | // print.h
111 | #ifndef PRINT
112 | #define PRINT
113 |
114 | void print();
115 |
116 | #endif // PRINT
117 | ```
118 |
119 | 这里的 `#ifndef ... #define ... #endif` 是头文件保护,防止同一头文件被 `#include` 两次造成重复声明的错误,
120 | 如果你不理解这部分也没关系,只需保证 `void print();` 这一行声明存在即可。
121 |
122 | - 在 main.c 和 print.c 中同时 `#include "print.h"`。
123 |
124 | 这样,程序就可以被编译运行了。
125 |
126 | 假设我们有以下三个文件:
127 |
128 | ```c
129 | // main.c
130 | #include "print.h"
131 |
132 | int main() {
133 | print();
134 | return 0;
135 | }
136 | ```
137 |
138 | ```c
139 | // print.c
140 | #include
141 |
142 | void print() {
143 | printf("Hello World!\n");
144 | }
145 | ```
146 |
147 | ```c
148 | // print.h
149 | #ifndef PRINT
150 | #define PRINT
151 |
152 | void print();
153 |
154 | #endif // PRINT
155 | ```
156 |
157 | 我们将依次编译链接,生成目标的二进制可执行程序。让我们看一下命令:
158 |
159 | ```console
160 | $ gcc main.c -c # 生成 main.o
161 | $ gcc print.c -c # 生成 print.o
162 | $ gcc main.o print.o -o main
163 | $ ./main
164 | Hello World!
165 | ```
166 |
167 | 这里我们使用了 `gcc -c`。
168 | `-c` 会将源文件编译为对象文件(Object file,.o 这一后缀就源自单词 object 的首字母)。
169 | 对象文件是二进制文件,不过它不可执行,因为其中需要引用外部代码的地方,是用占位数替代的,无法真正调用函数。
170 |
171 | 注意到我们没有添加 `-o` 选项,因为 `-c` 存在时 gcc 总会生成相同文件名(这里特指 basename,main.c 中的 main 部分)的 .o 对象文件。
172 |
173 | 生成了对象文件后,我们来进行链接,在相应函数调用的位置填上函数真正的地址,从而生成二进制可执行文件。
174 | `gcc` 这一指令会根据输入文件的类型调用相应的程序完成整个编译流程。
175 | 在这里,虽然同样是 gcc 指令,但是由于输入的为 .o 文件,gcc 将调用链接器进行链接,从而生成最终的可执行文件。
176 |
177 | 同样是这个原因,实际上使用 `gcc main.c print.c -o main` 是可以一步到位,但在接下来的内容里,你会看到另一个方案。
178 |
179 | !!! tip "gcc 的四个部分,编译的过程"
180 |
181 | gcc 的编译其实是四个过程的集合,分别是预处理(preprocessing)、编译(compilation)、汇编(assembly)、链接(linking),
182 | 分别由 cpp、cc1、as、ld 这四个程序完成,gcc 是它们的封装。
183 |
184 | 这四个过程分别完成:处理 `#` 开头的预编译指令、将源码编译为汇编代码、将汇编代码编译为二进制代码、组合众多二进制代码生成可执行文件,
185 | 也可分别调用 `gcc -E`、`gcc -S`、`gcc -c`、`gcc` 来完成。
186 |
187 | 在这一过程中,文件经历了如下变化:`main.c` 到 `main.i` 到 `main.s` 到 `main.o` 到 `main`。
188 |
189 | ### 使用构建工具(Build tools) {#c-build-tools}
190 |
191 | 上述方法在源文件较少时是比较方便的,但当我们面对的是数以千计万计的源文件(同样的,在工作或科研中这也是常见状况),我们将面临以下困难:
192 |
193 | - 手动地一一编译实在太麻烦,太浪费精力;
194 | - 这些源文件的编译有顺序要求,为了满足此依赖关系需要设计一个流程;
195 | - 编译整个项目需要难以忍受的大量时间,应当考虑到一部分未更改的源文件不需要重新编译。
196 |
197 | 为了让机器帮助程序员解决这些困难,构建工具应运而生。
198 | 同样的,由于需求巨大,构建工具在 Linux 上亦获得了强力支持。
199 |
200 | #### Makefile {#c-makefile}
201 |
202 | Makefile 是中小型项目常用的构建工具。
203 | 让我们考虑以下例子:
204 |
205 | 假设前述 3 份源文件已存在在当前目录下。
206 | 创建以下内容的文件,并命名为 `Makefile`:
207 |
208 | ```make
209 | main.o: main.c print.h
210 | print.o: print.c print.h
211 | main: main.o print.o
212 | ```
213 |
214 | 然后在当前目录下执行:
215 |
216 | ```console
217 | $ make main
218 | $ ./main
219 | Hello World!
220 | ```
221 |
222 | 为了解释这一过程,我们来分析一下 Makefile 的内容。其中:
223 |
224 | ```make
225 | main.o: main.c print.h
226 | ```
227 |
228 | 这一行,通过冒号分割,指定了一个名为 `main.o` 的目标,其依赖为 `main.c` 和 `print.h`。
229 | 由于整个文件中没有名为 `main.c` 的目标,所以 Makefile 会认为对应的 `main.c` 文件为一个依赖,`print.h` 同理。
230 |
231 | 在指定了目标和依赖后,紧接着的下一行如果用 **Tab** 缩进,则可以指定利用依赖获得目标的指令。
232 | 例如:
233 |
234 | ```make
235 | main.o: main.c print.h
236 | gcc main.c -c # 一定要用 Tab 缩进而不是 4 个 / 2 个空格——这是历史遗留问题。
237 | ```
238 |
239 | 以上内容表示如果要获得 `main.o` 这个目标,则会执行 `gcc main.c -c` 这个指令。
240 | 如果没有指定命令,Makefile 会尝试从文件后缀等处获取信息,推测你需要的指令。
241 | 例如此处即使不显式写出指令,Makefile 也知道用 gcc 来完成编译。
242 |
243 | 最终我们在 shell 中执行 `make main`,正是指定了一个最终目标。
244 | 如果不提供这个目标,Makefile 则会选择 Makefile 文件中第一目标。
245 | 为了获得最终目标,Makefile 会递归地获取依赖、执行指令。
246 |
247 | Makefile 的亮点在于引入了文件间的依赖关系。
248 | 在使用它进行构建时,Makefile 可以根据文件间的依赖关系和文件更新时间,找出需要重新编译的文件。
249 | 在项目较大时这能明显节省构建所需的时间,同时也能解决一些由于编译链接顺序造成的问题。
250 | 相较与输入一大串指令,单个的 `make [target]` 甚至是仅仅 `make`,也更加优雅和方便。
251 |
252 | !!! tip "小知识"
253 |
254 | 在 Makefile 中有一些隐含规则。即使我们的 Makefile 中没有显式书写这样的规则,make 也会按照这些隐含规则来运行。
255 | 例如,上文提到的自动将 `.c` 文件编译成 `.o` 就是一种隐含规则。
256 |
257 | 除此之外,Makefile 中还有如下隐含规则:
258 |
259 | - `filename.o` 的依赖会自动推导为 `filename.c`
260 | - `filename` 的依赖会自动推导为 `filename.o`
261 |
262 | 利用这两条隐含规则,我们的 Makefile 还可进一步化简成:
263 |
264 | ```make
265 | main.o: print.h
266 | print.o: print.h
267 | main: print.o
268 | ```
269 |
270 | #### 其他的构建工具:CMake,ninja…… {#c-build-tools-other}
271 |
272 | 一个更大的工程可能有上万、上十万份源文件,如果一一写进 Makefile,那依然会异常痛苦,且几乎不可能维护。
273 |
274 | 为了更好的构建程序,大家想出了“套娃”的办法:用一个程序来生成构建所需的配置,CMake 则在这一想法下诞生。
275 |
276 | CMake 在默认情况下,可以通过 `cmake` 命令生成 Makefile,再进一步进行 `make`。
277 |
278 | 对于 CMake 的讲解已经超出了本课程的讲解范围。
279 | CMake 作为一个足够成熟、也足够陈旧的工具,既有历史遗留问题,也有新时代下的新思路。
280 | 正如 C++ 和 Modern C++,CMake 也有 Modern CMake,更有像微软 vcpkg 那样新的辅助工具和解决方案。
281 | 如果你想了解 CMake 的一些知识,附录将会有简单的介绍,亦可以考虑看一些较新的、关于 Modern CMake 的博客,以及官方的最新文档。
282 |
283 | 另一个值得一提的是 ninja。ninja 和 Makefile、autoconf 较类似,是构建工具,所属抽象层次低于 CMake。
284 | ninja 的特点的是相较与 Makefile 更快,对于多线程编译的支持更好。
285 | 详细信息可以到 ninja 的官方网站查看。
286 |
287 | ### 至于 C++ {#c-for-cpp}
288 |
289 | C++ 的工具链与 C 的是相似的。
290 |
291 | 实际上,只需将上面内容中的 `gcc` 指令改为 `g++`,你就能同样地完成 C++ 的开发。
292 | gcc 这一编译器本身即支持多种编程语言,包括了 C、C++、Objective C 等。
293 | 其他编译器如 clang 也会提供 `clang++` 这样的指令完成 C++ 的编译。
294 | Makefile、CMake 这样的构建工具亦可以用于多种编程语言。
295 |
296 | ### 总结 {#c-conclusion}
297 |
298 | 在 Linux 下,大多编程语言都会提供一套适合命令行的、简单便捷的工具链。
299 | 善于运用这些工具,能够极大地提升你的开发效率,支持你完成自己的项目。
300 |
301 | ## Python 语言开发 {#py}
302 |
303 | Python 作为一门年长但恰逢新春的解释型语言,亦被业界广泛使用。
304 | 相较于 Windows,在 Linux 上开发 Python 要更加简单。
305 |
306 | 针对 Python 的介绍,我们将不会着力于具体代码,而是分析其一些外围架构,从而引出总结。
307 |
308 | ### 解释器 python {#py-interpreter}
309 |
310 | 一般的 Python(CPython)程序的运行,依靠的是 Python 解释器(Interpreter)。
311 | 在 Python 解释器中,Python 代码首先被处理成一种字节码(Bytecode,与 JVM 运行的字节码不是一个东西,但有相似之处),
312 | 然后再交由 PVM(Python virtual machine)进行执行,从而实现跨平台和动态等特性。
313 |
314 | 由于使用过于广泛,几乎每一份 Linux 都带有 Python 解释器,以命令 `python2` 或 `python3` 调用,分别对应两个版本的 Python。
315 |
316 | ### 包管理器 pip {#py-pip}
317 |
318 | 为使用外部的第三方包,Python 提供了一个包管理器:pip。
319 |
320 | pip 和 apt 之类的包管理器有相似之处:完成包的安装和管理,完成依赖的分析,等等。
321 | 不过 pip 管理的是 Python 包,可以在 Python 代码中使用这些包。让我们看下面的例子:
322 |
323 | ```console
324 | # 安装 Python 3 和 Python 3 的 pip。对于 Python 2 和 3 间的纠纠缠缠,我们将在之后讲解。
325 | $ sudo apt install python3 python3-pip
326 |
327 | # 测试一下看看,是否能够正常使用它们。
328 | # 请保证在 `python` 和 `pip` 后有 3 这个数字。这也是历史遗留问题。
329 | $ python3 -V
330 | $ pip3 -V
331 |
332 | # 暂时忽略以下两条指令,我们会在之后讲解。
333 | $ python3 -m venv venv
334 | $ source venv/bin/activate
335 | (venv)$ ls
336 | venv
337 |
338 | # 安装一个 Python 包 a、b,以及 a、b 依赖的 Python 包。
339 | (venv)$ pip3 install a b
340 |
341 | # 卸载一个 Python 包 b。注意:这不会删除之前一起安装的包 b 的依赖。
342 | (venv)$ pip3 uninstall b
343 | ```
344 |
345 | 安装了 a 之后,我们就能在代码中使用 a 这个包了。
346 |
347 | ```python3
348 | # main.py
349 | import a
350 |
351 | print(a)
352 | ```
353 |
354 | ```console
355 | (venv)$ python3 main.py
356 |
357 | ```
358 |
359 | 这样,我们就完成了对外部 Python 包的安装和引用。
360 |
361 | ### Python 依赖管理 {#py-dep}
362 |
363 | 一个软件一般含有众多依赖,尤其是对于追求易用、外部库众多的 Python 而言,使用外部库作为依赖是常事。
364 |
365 | 此处我们将尝试给出各种使用较多的 Python 依赖管理方案。
366 |
367 | #### requirements.txt {#py-requirements}
368 |
369 | 在一些项目下,你可能会发现一个名为 `requirement.txt` 的文件,里面是一行行的 Python 包名和一些对于软件版本的限制,例如:
370 |
371 | ```txt
372 | # requirements.txt
373 | django
374 | pytest>=3.0.0
375 | pytest-cov==1.0.0
376 | ```
377 |
378 | 为了安装这些 Python 包,使用以下指令:
379 |
380 | ```console
381 | $ pip3 install -r requirements.txt
382 | ```
383 |
384 | 这将从 `requirements.txt` 文件中逐行读取包名和版本限制,并由 pip 完成安装。
385 |
386 | 此方案简单明了,易于使用,但对于依赖的处理能力不足。
387 |
388 | #### setuptools: setup.py {#py-setup}
389 |
390 | 在 PyPI,即 pip 获取 Python 包的来源中,使用 setuptools 是主流选择。
391 | setuptools 不是 Python 官方的项目,但它已成为 Python 打包(packaging)的事实标准。
392 |
393 | 常见状况是目录下会有一个名为 `setup.py` 的文件。
394 | 要安装依赖,只需:
395 |
396 | ```console
397 | $ ls
398 | setup.py
399 | $ pip3 install .
400 | ```
401 |
402 | 这种方案特点是使用广泛,易于对接,能提供的信息和配置较全,但配置起来也较复杂。
403 |
404 | #### 其他的:pip-tools、pipenv…… {#py-dep-other}
405 |
406 | pip-tools 可以看作对 requirements.txt 的增强。
407 | 它额外提供了 `requirements.dev` 文件,从而完成了对于依赖进行版本锁定的支持。
408 |
409 | pipenv 则是一个更加全面的解决方案,它提供了类似于 npm 的配置文件和 lock 文件,对于依赖有非常强的管理功能。
410 | 但其完成度和工业中的稳定性尚有待证明。
411 |
412 | Python 有非常多的依赖管理方案,某种意义上讲是自带的 pip 管理功能不足所造成的。
413 | 一般而言,只需熟悉常用的 requirements.txt 和 setuptools 方案即可。
414 |
415 | ### Virtualenv {#py-venv}
416 |
417 | 让我们考虑以下情况:
418 |
419 | Python 通过包管理器如 apt 安装的包,默认安装在系统目录 `/usr/lib/python[version]` 下,
420 | 而通过 pip 安装的包,默认安装目录在 `/usr/local/lib/python[version]` 下,
421 | 当通过 pip 安装时显式传入了一个 `--user` 选项时,则会安装在用户目录 `~/.local/lib/python[version]` 下,
422 | 另外,在普通用户下直接执行 pip install 而没有传入 `--user` 选项时,也会因为用户没有系统目录的写权限而安装到用户目录。
423 | 当普通地运行 Python 解释器时,这几个目录下的包均可见。
424 |
425 | 现在假设用户目录下已有一个包 `a`,版本为 `1.0.0`。
426 | 现在我们需要开发一个程序,也需要包 `a`,但要求版本大于 `2.0.0`。
427 |
428 | 由于 pip 不允许同时安装不同版本的同一个包,当你运行 `pip3 install a>=2.0.0` 时,pip 会更新 `a` 到 `2.0.0`,
429 | 那原先依赖于 `a==1.0.0` 的软件就无法正常运行了。
430 |
431 | !!! tip "注意 `>=`"
432 |
433 | 在一些 Shell(如 zsh)中,`>=` 有特殊含义。
434 | 此时上述命令应用引号包裹 `>=` 部分,如 `pip3 install 'a>=2.0.0'`
435 |
436 | 为了解决这一问题,允许不同软件使用不同版本的包,Python 提供了 Virtualenv 这个工具。
437 | 其使用方法如下:
438 |
439 | 一般 Virtualenv 会带在默认安装的 Python 中。
440 | 如果没有,可以用 `sudo apt install python3-venv` 来安装。
441 |
442 | 常见的做法是使用 Python 的模块运行来完成在 Shell 中的执行:
443 |
444 | ```console
445 | $ python3 -m venv venv
446 | ```
447 |
448 | 以上指令中,`-m` 表示运行一个指定的模块,前一个 `venv` 指运行 venv 这个包的主模块 `__main__`,
449 | 后一个 `venv` 是参数,为生成目录的路径。
450 | 这将使 venv 在当前目录下生成一个名为 `venv` 的目录。
451 |
452 | 在一般的 shell 环境下,我们将使用 `source venv/bin/activate` 来启用这个 venv。
453 |
454 | 完成以上操作后,你就进入了当前目录下 venv 文件夹所对应的 Virtualenv。
455 | 此时,你使用 `pip3 install` 安装的 Python 包将会被安装在 venv 这个文件夹中,
456 | 这些包也只有在你 `source venv/bin/activate` 之后才可见,外部无法找到这些包。
457 | 通过 `deactivate` 可以退出 Virtualenv,回到之前的环境中。
458 |
459 | 实际上,由于 Python 是借助一些环境变量来完成包搜索的步骤的,`source venv/bin/activate`
460 | 其实是配置了一些环境变量,从而达到目的。这样,就实现了程序间依赖的隔离。
461 |
462 | ### Python 的版本 {#py-versions}
463 |
464 | 正如我们之前所讲,Python 不是一个新的编程语言。
465 | 现在的 Python,最新的版本已到 3.11(截至 2022 年末)。
466 | 实际上还在使用中的 Python,主要在 2.7 以及 3.5 以上这个区间内。
467 |
468 | Python 2 到 3 某种程度上讲不是变革,实际上 Python 2 和 3 基本可以看作两个不同的编程语言。
469 | 在从 2 到 3 的升级中,一方面众多底层语法都发生了改变,使得迁移异常麻烦。
470 | 另一方面,由于 Python 2 的盛行,程序 `python` 普遍指向 `python2`。
471 | 因此当 Python 3 出现时,为了有效区分两者,调用解释器时我们需要特地使用 `python3` 这一指令。
472 | 尽管在某些平台(例如 Arch 系 Linux)上,`python` 己经变为指向 `python3`,
473 | 但考虑到 Ubuntu、CentOS、Debian 等发行版上 `python` 有可能仍指向 `python2`,
474 | 显式地指定一个版本是更明智的选择。
475 |
476 | 实际上,Python 2 已在 2020 年初正式宣告停止维护,
477 | 现在如果我们要使用 Python,最好使用 3 版本。
478 |
479 | 而在 Python 3.x 版本中,截至 2024 年下半年,3.8 亦已经 EOL(end of life)。
480 |
481 | !!! tip "我应该选择哪个版本的 Python?"
482 |
483 | Python 3.x 已经迭代到一个相对稳定的阶段,如果你没有特殊需求,请使用 Python 3.x 的最新版本。
484 |
485 | 截止到 2024 年 5 月,我们推荐 Python 3.11。(或者使用系统自带的版本)
486 |
487 | 你可以在 [Status of Python versions](https://devguide.python.org/versions/) 查看 Python 各个版本的状态。
488 |
489 | ### Python 的其他实现 {#py-implementations}
490 |
491 | Python 作为一门编程语言,官方的实现是 CPython,我们一般使用的、成为事实标准的就是这个。
492 | CPython 中的 C 是指此解释器是用 C 实现。
493 |
494 | 相应的,Python 还有其他的一些实现:
495 |
496 | - JPython:将 Python 编译到 Java 字节码,由 JVM 来运行;
497 | - PyPy:相较于 CPython,实现了 JIT(just in time)编译器,性能有极大地提升;
498 | - Cython:引入了额外的语法和严密的类型系统,性能也有很大提升;
499 | - Numba:将 Python 编译到机器码,从而直接运行,性能也不错。
500 |
501 | 视情况使用不同的 Python 实现能够很大程度地提升性能。
502 | 但如果你不确定自己的意向,且性能需求不大,使用官方的 CPython 也是明智之选。
503 |
504 | ### 总结 {#py-conclusion}
505 |
506 | 外部包引用和依赖管理是程序开发中必不可少的部分。
507 | 如果官方有成熟的方案,跟随他们是明智的选择。
508 | 否则则需根据实际情况,按需选用。
509 |
510 | ## 思考题 {#questions}
511 |
512 | !!! question "试试 Rust"
513 |
514 | Rust 是一门新兴编译型编程语言。
515 | 尝试查询 Rust 的文档,指出 Rust 的编译器、依赖管理程序,
516 | 介绍一下如何将 Rust 源码变为可执行程序,如何在 Rust 中引用外部包。
517 |
518 | ## 引用来源 {#references .no-underline}
519 |
520 | [^1]: [Basics of the Unix Philosophy](https://homepage.cs.uri.edu/~thenry/resources/unix_art/ch01s06.html)
521 |
--------------------------------------------------------------------------------
/docs/Ch07/supplement.md:
--------------------------------------------------------------------------------
1 | ---
2 | icon: material/puzzle
3 | ---
4 |
5 | # 拓展阅读 {#supplement}
6 |
7 | ## Python 环境的另一种管理方式:Conda {#conda}
8 |
9 | Conda 是一个广泛使用的开源的包管理与环境管理系统。Miniconda 与 Anaconda 是两个广为人知的基于 Conda 的 Python 的发行版本。
10 |
11 | ### Anaconda 与 Miniconda {#anaconda-and-miniconda}
12 |
13 | Miniconda 和 Anaconda 都是开源的 Python 的发行版本。
14 |
15 | Miniconda 是 Anaconda 的免费迷你版本,只包含了 Conda、Python 及其依赖,以及少量其他有用的包,例如 pip 和 zlib。而 Anaconda 则额外包含了 250 多个自动安装的科学软件包,例如 SciPy 和 NumPy,并且测试了这些软件包之间的兼容性。Anaconda 分为个人版、商业版、团队版、企业版,除了个人版以外,其余版本均为付费产品。
16 |
17 | ### 安装 Miniconda {#install-miniconda}
18 |
19 | 从官网下载安装 Miniconda,并进入虚拟环境。
20 |
21 | ```console
22 | $ sh -c "$(wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -O -)"
23 |
24 | # 前面的选项可以保持默认,也可以自行修改
25 | please answer 'yes' or 'no':
26 | >>> yes
27 | # 选择 Miniconda 的安装路径
28 | Miniconda3 will now be installed into this location:
29 | >>> ~/.miniconda3
30 | # 添加配置信息到 ~/.bashrc 文件,这样每次打开终端时会自动激活虚拟环境
31 | Do you wish the installer to initialize Miniconda3 by running conda init? [yes|no]
32 | [no] >>> yes
33 |
34 | $ source ~/.bashrc # 应用配置文件信息,激活虚拟环境
35 | ```
36 |
37 | ### Conda 作为包管理器 {#conda-as-package-management-system}
38 |
39 | 类似于 pip,可以使用 `conda install` 来安装软件包。部分软件包既可以使用 pip 安装,也可以使用 conda 安装。
40 | 相比于 pip,conda 会执行更加严格的依赖检查,并且除了 Python 软件包外,还可以安装一些 C/C++ 软件包,例如 cudatoolkit、mkl 等。相对的,conda 支持的 Python 软件包的数量远少于 PyPI。
41 |
42 | ### Conda 作为环境管理器 {#conda-as-environment-management-system}
43 |
44 | 类似于 Virtualenv,可以使用 conda 来管理虚拟环境。
45 |
46 | 常见的使用方式如下:
47 |
48 | ```console
49 | $ conda create -n venv python=3.11 # 创建一个名为 venv,Python 版本为 3.11 的虚拟环境
50 | # 请确认虚拟环境已经成功创建
51 | $ conda activate venv # 切换到名为 venv 的虚拟环境
52 | $ conda deactivate # 退出当前虚拟环境
53 | ```
54 |
55 | ### 导出导入环境 {#export-import-env}
56 |
57 | 在一些 Python 项目中,你能找到一个 `environment.yml` 文件。
58 | 此文件类似于 `requirements.txt`,是 Conda 用以描述环境配置的文件。
59 | 你可以利用此文件来分享或复制环境,从而运行其他人的项目。
60 |
61 | `environment.yml` 文件不会自动生成。
62 | 为了获取当前环境所对应的 `environment.yml` 文件,你需要使用以下命令:
63 |
64 | ```console
65 | $ conda env export > environment.yml
66 | ```
67 |
68 | 此文件会包含当前环境下所有已装包的版本信息以便复现。
69 | 如果你只需要导出明确由用户自己安装的包、而不包含这些包的依赖,可以使用 `--from-history` 选项。
70 |
71 | 通过 `environment.yml` 文件,你可以使用以下命令来复现环境:
72 |
73 | ```console
74 | $ conda env create -f environment.yml
75 | ```
76 |
77 | 复现出的环境的名字与原环境相同、由 `environment.yml` 文件的 `name` 字段传递。
78 | 相似的,环境的存放位置由 `prefix` 字段传递。
79 |
80 | ??? info "由 pip 安装的包"
81 |
82 | 使用 `--from-history` 选项时,由 pip 安装的包不会被包含在 `environment.yml` 文件中。
83 | 而在不使用此选项的一般情况下,由 pip 安装的包会被记录在 `environment.yml` 文件中的 `pip` 列表内,因而可以被复现。
84 |
85 | ## 动态链接与静态链接 {#dynamic-or-static-link}
86 |
87 | 在大部分情况下,我们编译的程序都是动态链接的。动态链接在这里指程序文件还依赖其他库文件,可以使用 `ldd` 命令确认:
88 |
89 | ```console
90 | $ cat hello.c
91 | #include
92 |
93 | int main() {
94 | puts("Hello, world!");
95 | return 0;
96 | }
97 | $ gcc -o hello hello.c
98 | $ ldd ./hello
99 | linux-vdso.so.1 (0x00007ffc49703000)
100 | libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f36767d3000)
101 | /lib64/ld-linux-x86-64.so.2 (0x00007f36769ea000)
102 | ```
103 |
104 | 这里,我们编译得到程序就依赖于 `linux-vdso.so.1`、`libc.so.6` 和 `/lib64/ld-linux-x86-64.so.2` 三个库文件,如果系统中没有这三个库文件,程序就无法执行。
105 |
106 | ??? info "这三个文件的用途"
107 |
108 | 可以看到,即使是 Hello, world 这么简单的程序,也需要外部库文件。
109 |
110 | - linux-vdso.so.1:这个库是为了减小用户程序调用系统调用产生的切换模式(用户态 -> 内核态 -> 用户态)的开销而设计的。这个文件事实上并不存在。在内核加载程序时,这一部分会被自动加载入程序内存中。详情可参考 vdso(7) 文档。
111 | - libc.so.6:C 运行时库,提供各种 C 函数的实现。
112 | - ld-linux-x86-64.so.2:动态链接加载器。当程序需要动态链接库中的函数时负责查找并加载对应的函数。
113 |
114 | 我们在编写程序时,有时需要使用到第三方的库,此时需要加上 `-l` 参数指定在**链接**时链接到的库。
115 |
116 | ```console
117 | $ gcc -o thread thread.c -lpthread # 编译一个依赖于 pthread 线程库的应用
118 | $ ldd ./thread # 可以看到多出了 libpthread.so.0 的动态链接依赖
119 | linux-vdso.so.1 (0x00007ffe6ad93000)
120 | libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fad173c7000)
121 | libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fad171d5000)
122 | /lib64/ld-linux-x86-64.so.2 (0x00007fad17407000)
123 | ```
124 |
125 | 对于复杂的应用来说,下载后可能会因为没有动态链接库而无法运行。这一点在打包、分发自己编写的程序时也要特别注意。
126 |
127 | ```console
128 | $ ldd MegaCli64
129 | linux-vdso.so.1 (0x00007ffca1868000)
130 | libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fa77f6c9000)
131 | libncurses.so.5 => not found
132 | libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fa77f6c3000)
133 | libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007fa77f4e1000)
134 | libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fa77f392000)
135 | libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fa77f375000)
136 | libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fa77f183000)
137 | /lib64/ld-linux-x86-64.so.2 (0x00007fa77f704000)
138 | $ ./MegaCli64 # 缺少 libncurses.so.5,从而无法执行
139 | ./MegaCli64: error while loading shared libraries: libncurses.so.5: cannot open shared object file: No such file or directory
140 | ```
141 |
142 | 而静态链接则将依赖的库全部打包到程序文件中。
143 |
144 | ```console
145 | $ gcc -o hello-static hello.c -static # 编译一个静态链接的应用
146 | $ ldd ./hello-static # 没有动态链接库的依赖
147 | not a dynamic executable
148 | ```
149 |
150 | 此时编译得到的程序文件没有额外的依赖,在其他机器上一般也能顺利运行。近年来流行的 Go 语言的一大特点也是静态链接,编译得到的程序有着很好的兼容性。
151 |
152 | 但是静态链接也存在一些问题。首先是程序大小,比较一下前面编译的 hello-static 和 hello 的大小吧:
153 |
154 | ```console
155 | $ ls -l hello hello-static
156 | -rwxrwxr-x 1 ustc ustc 17K Feb 28 14:43 hello
157 | -rwxrwxr-x 1 ustc ustc 852K Feb 28 14:39 hello-static
158 | ```
159 |
160 | 可以看到,动态链接的程序比较小,而静态链接的程序大小接近 1M,况且这还只是一个最简单的 hello world!并且在大小方面,静态链接的程序在运行时无法共享运行库的内存,从而导致内存占用也有一定程度增加。
161 |
162 | 其次,当运行库出现问题时,用户可以选择更新库,此时所有动态链接的程序都能得到修复,但是静态链接的程序由于不使用系统库,就不得不一个个重新编译。
163 |
164 | 最后一点是,Linux 的默认 C 库 glibc 对静态链接并不友好,如果程序使用了一部分函数,在静态链接时会受到限制。
165 |
166 | ```console
167 | $ gcc -o getaddrinfo-example getaddrinfo.c -static
168 | /usr/bin/ld: /tmp/ccPZTyKT.o: in function `main':
169 | getaddrinfo.c:(.text+0xd2): warning: Using 'getaddrinfo' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
170 | ```
171 |
172 | !!! tip "Musl libc"
173 |
174 | 近年来,musl libc 开始流行,并且它也是 Alpine Linux 的 C 运行时库。它的特点是轻量、快速、对静态链接友好。我们也可以来试一下:
175 |
176 | ```console
177 | $ sudo apt install musl-tools # musl-tools 提供了 musl-gcc 等方便的编译工具
178 | $ musl-gcc -o hello-static-musl hello.c -static # 静态链接 musl libc
179 | $ ls -lha hello hello-static hello-static-musl
180 | -rwxrwxr-x 1 ustc ustc 17K Feb 28 14:43 hello
181 | -rwxrwxr-x 1 ustc ustc 852K Feb 28 14:39 hello-static
182 | -rwxrwxr-x 1 ustc ustc 26K Feb 28 15:00 hello-static-musl
183 | $ # 可以看到静态链接 musl 得到的文件与动态链接接近,并且远小于静态链接 glibc 得到的文件。
184 | $ musl-gcc -o getaddrinfo-example-musl getaddrinfo.c -static
185 | $ # musl libc 的 getaddrinfo() 实现不依赖于额外的系统组件,所以可以正常静态链接
186 | ```
187 |
188 | ## 交叉编译示例 {#cross-compile-example}
189 |
190 | 有时候,我们需要为其他的平台编写程序,例如:
191 |
192 | - 我正在使用的电脑是 x86_64 架构的,但是我现在需要给树莓派编写程序(体系结构不同)。
193 | - 我正在使用 Linux,但是我现在需要编译出一个 Windows 程序(操作系统不同)。
194 |
195 | 怎么办呢?只能用虚拟化程序运行目标架构,然后在上面跑编译了吗?这样会很麻烦、速度可能会很慢,甚至有的时候不可行(例如性能低下的嵌入式设备,可能连编译器都加载不了)。
196 |
197 | 这时候就需要交叉编译了。对于常见的架构,Ubuntu/Debian 提供了对应的交叉编译器,很大程度方便了使用。以下将给出交叉编译简单的示例。
198 |
199 | ### 在 x86_64 架构编译 aarch64 的程序 {#x86_64-cross-compile-aarch64}
200 |
201 | Aarch64 是 ARM 指令集的 64 位架构。
202 |
203 | ```console
204 | $ sudo apt install gcc-aarch64-linux-gnu # 安装交叉编译到 aarch64 架构的编译器,同时也会安装对应架构的依赖库
205 | $ aarch64-linux-gnu-gcc -o hello-aarch64 hello.c # 直接编译即可
206 | $ file hello-aarch64 # 看一下文件信息,可以看到是 aarch64 架构的
207 | hello-aarch64: ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, BuildID[sha1]=09d2ad67b8e2f3b4befe3ce846182743d27910db, for GNU/Linux 3.7.0, not stripped
208 | $ ./hello-aarch64 # 无法直接运行,因为架构不兼容
209 | -bash: ./hello-aarch64: cannot execute binary file: Exec format error
210 | $ sudo apt install qemu-user-static # 安装 qemu 模拟器
211 | $ qemu-aarch64-static ./hello-aarch64 # 使用 aarch64 模拟器运行程序
212 | /lib/ld-linux-aarch64.so.1: No such file or directory
213 | $ # 为什么仍然无法运行?这是因为 qemu 不知道从哪里找运行时库
214 | $ # 需要补充 QEMU_LD_PREFIX 环境变量
215 | $ QEMU_LD_PREFIX=/usr/aarch64-linux-gnu/ qemu-aarch64-static ./hello-aarch64
216 | Hello, world!
217 | ```
218 |
219 | ### 在 Linux 下编译 Windows 程序 {#linux-cross-compile-windows}
220 |
221 | 这里使用 mingw 来进行交叉编译。
222 |
223 | ```console
224 | $ sudo apt install gcc-mingw-w64 # 安装 mingw 交叉编译器
225 | $ sudo apt install wine # 安装 wine Windows 兼容层(默认仅安装 64 位架构支持)
226 | $ x86_64-w64-mingw32-gcc -o hello.exe hello.c # 编译为 64 位的 Windows 程序
227 | $ file hello.exe # 确认为 Windows 程序
228 | hello.exe: PE32+ executable (console) x86-64, for MS Windows
229 | $ wine hello.exe # 使用 wine 运行
230 | it looks like wine32 is missing, you should install it.
231 | as root, please execute "apt-get install wine32"
232 | wine: created the configuration directory '/home/ubuntu/.wine'
233 | (忽略首次配置的输出)
234 | wine: configuration in L"/home/ubuntu/.wine" has been updated.
235 | Hello, world!
236 | ```
237 |
238 | MinGW 也可以编译 Windows 下的图形界面应用程序。以下的程序例子来自 [Windows Hello World Sample](https://docs.microsoft.com/en-us/windows/win32/learnwin32/windows-hello-world-sample)(MIT License),稍作修改以符合 C 语言的语法。
239 |
240 | ```c
241 | // winhello.c
242 | #ifndef UNICODE
243 | #define UNICODE
244 | #endif
245 |
246 | #include
247 |
248 | LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
249 |
250 | int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE _, PWSTR pCmdLine, int nCmdShow)
251 | {
252 |
253 | // Register the window class.
254 | const wchar_t CLASS_NAME[] = L"Sample Window Class";
255 |
256 | WNDCLASS wc = { };
257 |
258 | wc.lpfnWndProc = WindowProc;
259 | wc.hInstance = hInstance;
260 | wc.lpszClassName = CLASS_NAME;
261 |
262 | RegisterClass(&wc);
263 |
264 | // Create the window.
265 |
266 | HWND hwnd = CreateWindowEx(
267 | 0, // Optional window styles.
268 | CLASS_NAME, // Window class
269 | L"Learn to Program Windows", // Window text
270 | WS_OVERLAPPEDWINDOW, // Window style
271 |
272 | // Size and position
273 | CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
274 |
275 | NULL, // Parent window
276 | NULL, // Menu
277 | hInstance, // Instance handle
278 | NULL // Additional application data
279 | );
280 |
281 | if (hwnd == NULL)
282 | {
283 | return 0;
284 | }
285 |
286 | ShowWindow(hwnd, nCmdShow);
287 |
288 | // Run the message loop.
289 | MSG msg = { };
290 | while (GetMessage(&msg, NULL, 0, 0))
291 | {
292 | TranslateMessage(&msg);
293 | DispatchMessage(&msg);
294 | }
295 |
296 | return 0;
297 | }
298 |
299 | LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
300 | {
301 | switch (uMsg)
302 | {
303 | case WM_DESTROY:
304 | PostQuitMessage(0);
305 | return 0;
306 |
307 | case WM_PAINT:
308 | {
309 | PAINTSTRUCT ps;
310 | HDC hdc = BeginPaint(hwnd, &ps);
311 |
312 | // All painting occurs here, between BeginPaint and EndPaint.
313 | FillRect(hdc, &ps.rcPaint, (HBRUSH) (COLOR_WINDOW+1));
314 | EndPaint(hwnd, &ps);
315 | }
316 | return 0;
317 | }
318 |
319 | return DefWindowProc(hwnd, uMsg, wParam, lParam);
320 | }
321 | ```
322 |
323 | ```console
324 | $ x86_64-w64-mingw32-gcc -o winhello.exe winhello.c
325 | /usr/bin/x86_64-w64-mingw32-ld: /usr/lib/gcc/x86_64-w64-mingw32/9.3-win32/../../../../x86_64-w64-mingw32/lib/libmingw32.a(lib64_libmingw32_a-crt0_c.o): in function `main':
326 | ./build/x86_64-w64-mingw32-x86_64-w64-mingw32-crt/./mingw-w64-crt/crt/crt0_c.c:18: undefined reference to `WinMain'
327 | collect2: error: ld returned 1 exit status
328 | $ # 编译失败,这是因为编译 Windows Unicode(UTF-16)程序需要额外的参数 -municode。
329 | $ # 参见 https://sourceforge.net/p/mingw-w64/wiki2/Unicode%20apps/
330 | $ x86_64-w64-mingw32-gcc -o winhello.exe winhello.c -municode
331 | $ wine winhello.exe # 需要在图形界面下执行,或者复制到 Windows 中执行亦可
332 | ```
333 |
--------------------------------------------------------------------------------
/docs/Ch08/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | icon: simple/docker
3 | ---
4 |
5 | # Docker
6 |
7 | !!! Warning "本文已基本完稿,正在审阅和修订中,不是正式版本。"
8 |
9 | !!! abstract "导言"
10 |
11 | 「容器」,是近年来非常热门的一个概念。它通过操作系统内核提供的隔离技术,实现轻量级的虚拟化环境。目前,它在软件的开发、部署等方面有着非常广泛的应用。
12 |
13 | 而 Docker,是 Linux 容器技术中的代表性软件,它为用户提供了方便的接口来创建、使用 Linux 容器。下面,就让我们简单地入门一下 Docker。
14 |
15 | ## 为什么使用 Docker? {#why-docker}
16 |
17 | Docker 能够利用 Linux 内核的容器特性,隔离出一个轻便的环境来运行程序。这有什么意义呢?试想以下这些情况:
18 |
19 | - 你运行的 Linux 发行版很老,而你需要运行一个更新版本的 Linux 发行版,或者完全不同的 Linux 发行版设计的程序。
20 | - 你和朋友在设计一个大型的程序,而因为你们配置的环境不同,有时候在某个人的机器上正常运行的程序,在另一台机器上没法正常运行。
21 | - 你希望在多台服务器上部署一个项目,但是项目需要非常复杂的配置,一个一个配置服务器的成本非常大。
22 | - …………
23 |
24 | Docker 就可以帮助解决这些问题。它可以快速配置不同的环境(比如说,通过 Docker,你可以在 Ubuntu 上使用 CentOS 的环境),部署应用。
25 |
26 | ## 安装 Docker {#install-docker}
27 |
28 | Docker 可以在 Windows, Linux 和 macOS 上安装。下面我们讨论内容都基于 Docker 免费的社区版本。
29 |
30 | ### 在 Windows 或 macOS 上安装 {#install-on-windows-or-macos}
31 |
32 | 上面提到,Docker 使用了 Linux 内核的容器特性,它依赖于 Linux。所以在 Windows 和 macOS 上,Docker 不得不通过虚拟 Linux 内核的方式来完成任务。其提供了一套被称为 Docker Desktop 的软件来帮助在 Windows 和 macOS 上配置 Docker。直接从[官网下载](https://www.docker.com/products/docker-desktop)即可。
33 |
34 | !!! info "Docker Desktop on Windows 的环境要求"
35 |
36 | Docker Desktop on Windows [对系统有一定的环境要求](https://docs.docker.com/desktop/windows/install/#system-requirements),以便虚拟化运行 Linux。如果环境要求无法达到,可以安装[老版本的 Docker Toolbox on Windows](https://docs.docker.com/toolbox/toolbox_install_windows/)。
37 |
38 | !!! note "Windows 容器"
39 |
40 | 你可能会搜索到,Docker 也支持「Windows 容器」。是的,在新版本(1607 之后)的 Windows 10 中,Windows 内核支持 Windows 容器。可以在这样的容器中运行 Windows 程序。如果你感兴趣,可以阅读[微软的 Containers on Windows Documentation](https://docs.microsoft.com/en-us/virtualization/windowscontainers/) 和 [Docker Windows Containers 的介绍](https://www.docker.com/products/windows-containers)。这样的容器无法运行 Linux 程序,下面也不会涉及到。
41 |
42 | ### 在 Linux 上安装 {#install-on-linux}
43 |
44 | 各大发行版的软件源包含 Docker,也可以跟从[官方文档](https://docs.docker.com/install/linux/docker-ce/debian/),安装其提供的 Docker 社区版本。
45 |
46 | !!! info "docker, docker.io 和 docker-ce"
47 |
48 | 首先,在 Debian/Ubuntu 上,**不要运行 `sudo apt install docker`**。现在,这里安装的 `docker` 是一个系统托盘程序,和本章的 Docker **没有任何关系**。
49 |
50 | `docker.io` 是由 Debian/Ubuntu 维护的 Docker 版本,和软件源里的其他程序一样,它比官方的最新版本要稍微老一些。它所依赖的程序库由 Debian/Ubuntu 软件源中的其他软件包提供。
51 |
52 | 而 `docker-ce` 是由 Docker 官方维护的。它依赖的程序库都被打包在了这个包中。本章中,可以安装这两个版本中的任意一个。
53 |
54 | Docker 官方提供了安装 Docker 社区版本的简易安装脚本:
55 |
56 | ```console
57 | $ curl -fsSL https://get.docker.com -o get-docker.sh
58 | $ sudo sh get-docker.sh
59 | ```
60 |
61 | !!! warning "不要在 WSL1 上安装 Docker"
62 |
63 | WSL1 在 Windows 上提供了方便的 Linux 环境。但很遗憾,Docker 的核心服务无法在 WSL1 上运行,直接安装是无法使用的。虽然可以把 WSL 中的 Docker 的命令行工具连接到 Docker for Windows 的核心服务上,但是比较麻烦,这里不推荐这样做。
64 |
65 | 如果你正在使用 WSL2,可以安装 Docker,并且使用 Windows 下的 Docker Desktop 进行管理,详细信息可阅读 [Using Docker in WSL 2](https://code.visualstudio.com/blogs/2020/03/02/docker-in-wsl2)。
66 |
67 | 在安装完成后,可以使用
68 |
69 | ```console
70 | $ sudo adduser 用户名 docker
71 | ```
72 |
73 | 将需要使用 Docker 的用户[加入](../Ch05/index.md#adduser) `docker` 用户组。**注意:`docker` 用户组中的用户拥有与 root 等效的权限。**
74 |
75 | ### 配置 Registry Mirror(可选,推荐) {#setup-registry-mirror}
76 |
77 | !!! tip "本节操作请参考其他文档"
78 |
79 | 你可以参考 获取目前可能可以使用的 registry mirror。
80 |
81 | ### 使用 Hello World 测试 Docker 安装 {#verify-docker-setup}
82 |
83 | Docker 官方提供了最精简的 `hello-world` 镜像,可以用来验证 Docker 安装是否正确、是否正常工作。尝试运行 `docker run --rm hello-world` 看看吧。
84 |
85 | ```text
86 | $ docker run --rm hello-world
87 |
88 | Hello from Docker!
89 | This message shows that your installation appears to be working correctly.
90 |
91 | To generate this message, Docker took the following steps:
92 | 1. The Docker client contacted the Docker daemon.
93 | 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
94 | (amd64)
95 | 3. The Docker daemon created a new container from that image which runs the
96 | executable that produces the output you are currently reading.
97 | 4. The Docker daemon streamed that output to the Docker client, which sent it
98 | to your terminal.
99 |
100 | To try something more ambitious, you can run an Ubuntu container with:
101 | $ docker run -it ubuntu bash
102 |
103 | Share images, automate workflows, and more with a free Docker ID:
104 | https://hub.docker.com/
105 |
106 | For more examples and ideas, visit:
107 | https://docs.docker.com/get-started/
108 | ```
109 |
110 | 如果你看到了像上面这样的输出,说明你安装的 Docker 已经一切准备就绪,可以使用了。
111 |
112 | ## 使用 Docker 容器 {#use-docker}
113 |
114 | 接下来我们来尝试几个例子,体验 Docker 环境的独立性与易用性。
115 |
116 | ### 在 Ubuntu 容器中使用 shell {#use-ubuntu-bash}
117 |
118 | - `docker run -it --rm --name ubuntu-container ubuntu:20.04`
119 |
120 | 这里,`--rm` 代表容器停止运行(退出)之后,会被立刻删除;`--name` 参数代表给容器命名,如果没有加这个参数,那么 docker 会给容器随机起一个格式类似于 gracious_brahmagupta 的名字。
121 |
122 | `-it` 是为了获得可交互的 Shell 所必须的。`-i` 会将容器的 init(主进程,这里是 `/bin/bash`)的标准输入与 `docker` 这个程序的标准输入相连接;而 `-t` 会告知主进程输入为终端(TTY)设备。
123 |
124 | 在执行以上命令之后,你会获得一个 Ubuntu 20.04 的容器环境,退出 Shell 之后容器就会被销毁。
125 |
126 | 如果没有加上 `--rm`,退出后可以使用 `docker ps -a` 查看系统中所有的容器。
127 |
128 | ```console
129 | $ sudo docker ps -a
130 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
131 | 39d8ef1d4acf ubuntu:20.04 "bash" 4 seconds ago Exited (0) 2 seconds ago ubuntu-container
132 | ```
133 |
134 | 之后使用 `docker start` 启动容器。
135 |
136 | ```console
137 | $ sudo docker start -ai ubuntu-container
138 | root@39d8ef1d4acf:/#
139 | ```
140 |
141 | `-a` 代表连接输出以及信号。最后的 `ubuntu-container` 代指我们刚刚创建的那个容器。也可以输入容器的 ID 来启动(不需要输入完整的 ID,只需要前几位即可):
142 |
143 | ```console
144 | $ sudo docker start -ai 39d
145 | root@39d8ef1d4acf:/#
146 | ```
147 |
148 | 如果忘记加上了参数直接启动,也可以使用 `docker attach` 将容器的主进程的输入输出接上。
149 |
150 | ```console
151 | $ sudo docker attach ubuntu-container
152 | root@39d8ef1d4acf:/#
153 | ```
154 |
155 | `docker exec` 也可以完成相似的事情:它可以在容器中执行指定的命令(当然也包括 Shell 了)。
156 |
157 | ```console
158 | $ sudo docker exec -it ubuntu-container bash
159 | root@39d8ef1d4acf:/#
160 | ```
161 |
162 | 由于 `docker exec` 创建的进程不是主进程,退出后容器也不会退出,适合需要调试容器的场合。
163 |
164 | 与 `docker start` 相对应,`docker stop` 可以关闭一个容器,`docker rm` 可以删除一个容器。
165 |
166 | ```console
167 | $ sudo docker stop ubuntu-container
168 | 39d
169 | $ sudo docker rm ubuntu-container
170 | 39d
171 | ```
172 |
173 | ### 在 Python 容器中使用 Python 命令行 {#use-python-repl}
174 |
175 | - `docker run -it --name python3 python`
176 |
177 | 与上面的例子类似,执行之后会获得一个 Python 3 最新版本的环境。这里我们通过 `--name` 将创建的容器命名为 `python3`。
178 |
179 | ### 在 MkDocs 容器中构建本书 {#use-mkdocs-material-build}
180 |
181 | - 从 GitHub 上获取本书源码:`git clone https://github.com/ustclug/Linux101-docs.git`
182 | - `docker run --rm -v ${PWD}/Linux101-docs:/docs -p 8000:8000 squidfunk/mkdocs-material`
183 |
184 | 在执行完成之后,可以使用浏览器访问本地的 8000 端口,以查看构建结果。
185 |
186 | 这里多出了两个参数:
187 |
188 | - `-v`: 代表将本地的文件(夹)「挂载」(实际是 bind mount)到容器的对应目录中(这里是 `/docs`)。注意这个参数只接受绝对路径,所以这里读取了 `PWD` 这个变量,通过拼接的方式拼出绝对路径。
189 | - `-p 8000:8000`: 代表将容器的 8000 端口暴露在主机的 8000 端口上,否则容器外部访问不了 8000 端口。
190 | - 另外,我们不需要在终端中与容器中的进程进行交互,所以没有设置 `-it` 参数。
191 |
192 | ## 构建自己的 Docker 镜像 {#build-docker-image}
193 |
194 | ### 手工构建镜像 {#build-manually}
195 |
196 | `docker commit` 命令可以从当前运行的容器新建镜像。以下是一个简单的例子:
197 |
198 | ```console
199 | $ sudo docker run -it ubuntu
200 | root@82d245a5a4a1:/# apt update && apt install curl
201 | (输出省略)
202 | root@82d245a5a4a1:/# exit
203 | exit
204 | $ sudo docker ps -a
205 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
206 | 82d245a5a4a1 ubuntu "/bin/bash" 2 minutes ago Exited (0) 23 seconds ago laughing_elgamal
207 | $ sudo docker commit 82d245a5a4a1
208 | sha256:fe0a84d81b867949b27bacec4794303852b05ae76df14818bae85751b14f6e20
209 | $ sudo docker images
210 | REPOSITORY TAG IMAGE ID CREATED SIZE
211 | fe0a84d81b86 54 seconds ago 116MB
212 | $ sudo docker save fe0a84d81b86 > example.tar
213 | $ ls -lh example.tar
214 | -rw-r--r-- 1 ustc ustc 114M Feb 10 17:48 example.tar
215 | ```
216 |
217 | 得到的 example.tar 即为我们的 Docker 镜像。可以使用 `docker load < example.tar` 的方式在其他环境中加载。但是,从可维护性等方面考虑,我们更推荐以下使用 Dockerfile 的做法。
218 |
219 | ### 使用 Dockerfile 自动化构建 {#build-with-dockerfile}
220 |
221 | Dockerfile 是构建 Docker 镜像的标准格式,下面会举一些例子。我们会基于这些例子简单介绍 Dockerfile 的语法。
222 |
223 | #### 构建简单的交叉编译环境 {#cross-compile-example}
224 |
225 | 这个例子尝试使用 Debian 仓库中的 RISC-V [交叉编译](../Ch07/supplement.md#cross-compile-example)工具链与 QEMU 模拟器构建一个简单的用于交叉编译的环境。
226 |
227 | ```Dockerfile
228 | FROM debian:buster-slim
229 |
230 | RUN sed -i 's/deb.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list && \
231 | apt update && apt install -y --no-install-recommends \
232 | gcc-riscv64-linux-gnu g++-riscv64-linux-gnu libc6-dev-riscv64-cross \
233 | binutils-riscv64-linux-gnu libstdc++-dev-riscv64-cross \
234 | qemu-system-misc qemu-user-static qemu-user binfmt-support \
235 | fish vim
236 |
237 | WORKDIR /workspace/
238 | ENV QEMU_LD_PREFIX=/usr/riscv64-linux-gnu/
239 |
240 | CMD ["fish"]
241 | ```
242 |
243 | 通过使用 `docker build`,我们可以构建出镜像。
244 |
245 | ```console
246 | sudo docker build -t riscv-cross:example .
247 | ```
248 |
249 | `-t riscv-cross:example` 代表为这个镜像打上 `riscv-cross:example` 的标签。构建完成后,使用 `docker run` 执行即可:
250 |
251 | ```console
252 | $ sudo docker run -v ${PWD}/workspace:/workspace -it riscv-cross:example
253 | Welcome to fish, the friendly interactive shell
254 | root@dec3d33003ee /workspace# vim helloworld.c
255 | root@dec3d33003ee /workspace# riscv64-linux-gnu-gcc helloworld.c
256 | root@dec3d33003ee /workspace# qemu-riscv64 ./a.out
257 | Hello, world!
258 | ```
259 |
260 | 从这个例子中,我们可以看到 Dockerfile 的一些指令:
261 |
262 | 1. `FROM` 定义了基础镜像,之后执行的命令都是在基础镜像之上进行操作。如果希望基础镜像为空,可以使用 `FROM scratch`。
263 | 2. `RUN` 指定了在镜像中执行的命令。可以注意到,我们将多个命令合并在了一起执行,这有助于减小镜像的冗余大小。
264 | 3. `WORKDIR` 可以切换当前的所在的工作目录。
265 | 4. `ENV` 指定了当前的环境变量。
266 | 5. `CMD` 指定了容器启动时执行的命令。
267 |
268 | Docker 在根据 Dockerfile 构建时,会从上到下执行这些指令,每条指令对应镜像的一层。Docker 容器镜像的独特之处就在于它的分层设计:在构建镜像时每层的更改会叠加在上一层上(这意味着,上一层的所有数据仍然会保留,即使在新的一层删除了);如果某一层已经存在,Docker 会直接使用这一层,节约构建的时间和占用的空间。
269 |
270 | ??? tips "镜像分层是如何实现的?"
271 |
272 | Docker 默认使用 OverlayFS 存储镜像。Overlay 文件系统允许用户将一个目录挂在另一个只读的目录上,所有修改都记录在可写的上层上。这种特性在 Linux LiveCD 中非常有用:可以将只读的 LiveCD 和硬盘上的目录使用 Overlay 文件系统挂载在一起,然后所有的修改都可以存储在硬盘上。
273 |
274 | !!! tip "尽量减少 Docker 镜像的层数"
275 |
276 | 因为 Docker 容器镜像的分层设计,上面提到,`RUN` 中将多个命令通过 `&&` 的方式合并在了一起执行,有助于减小镜像的冗余大小。Dockerfile 不是 Shell 脚本,请避免每条命令都使用单独的 `RUN` 来执行的写法,以下是一个生产环境的错误例子节选(删除了某些部分以方便展示),**无论如何,请不要这么写 Dockerfile**:
277 |
278 | ```Dockerfile
279 | FROM centos:7
280 |
281 | RUN yum -y install wget make yum-utils
282 |
283 | RUN yum-builddep python -y
284 |
285 | RUN yum -y install gcc
286 | RUN yum -y install vim
287 | RUN yum -y install mariadb-devel
288 |
289 | RUN wget -O /tmp/Python-3.7.3.tgz https://www.python.org/ftp/python/3.7.3/Python-3.7.3.tgz
290 | RUN tar -zxvf /tmp/Python-3.7.3.tgz -C /tmp/
291 | RUN /tmp/Python-3.7.3/configure
292 | RUN make && make install
293 |
294 | RUN rm -f /usr/bin/python
295 | RUN ln -s /usr/local/bin/python3 /usr/bin/python
296 | RUN ln -s /usr/local/bin/pip3 /usr/bin/pip
297 |
298 | RUN pip install --upgrade pip
299 |
300 | RUN sed -i 's/python/python2/' /usr/bin/yum
301 |
302 | RUN rm -rf /tmp/Python-3.7.3*
303 | RUN yum clean all
304 | ```
305 |
306 | 当然,这不等于说必须要把所有命令都写在一条 `RUN` 里面。对于执行时间很长的命令,可以考虑放在 Dockerfile 的开头,并且使用单独的 `RUN` 运行,因为 Docker 在构建镜像时,可以重复使用之前构建好的层。这么做可以节约构建与调试 Dockerfile 的时间。
307 |
308 | #### 在生产环境中运行使用 Flask 编写的简单网站 {#flask-production-example}
309 |
310 | Flask 是一个知名的 Python web 框架。本例子包含了一个运行 Flask 编写的网站的简单 Dockerfile(不包含数据库等部分):
311 |
312 | ```dockerfile
313 | FROM tiangolo/uwsgi-nginx-flask:python3.8
314 |
315 | RUN pip3 config set global.index-url https://mirrors.bfsu.edu.cn/pypi/web/simple
316 | RUN pip3 install pyopenssl
317 |
318 | COPY ./app /app
319 | ```
320 |
321 | 这里使用了 `COPY` 指令,将本地的 `app` 目录复制进容器镜像的 `/app` 中。
322 |
323 | ## 使用 Docker Compose 自动运行容器 {#docker-compose}
324 |
325 | Docker Compose 是一个方便的小型容器编排工具。如果前面安装的是 `docker.io` 软件包,那么系统中可能未安装 `docker-compose`,使用以下命令安装:
326 |
327 | ```console
328 | $ sudo apt install docker-compose
329 | ```
330 |
331 | ### 使用 Docker Compose 创建 WordPress 博客 {#use-docker-compose-build-wordpress-with-mysql}
332 |
333 | WordPress 是一个知名的博客应用。本例子使用 Docker Compose,创建了一个使用 MySQL 数据库的 WordPress。
334 |
335 | 新建一个文件夹,在其中放入一个名为 `docker-compose.yml` 的配置文件:
336 |
337 | ```yaml
338 | version: "3"
339 | services:
340 | db:
341 | image: mysql:5.7
342 | container_name: wordpress_db
343 | restart: always
344 | volumes:
345 | - ./mysql:/var/lib/mysql
346 | environment:
347 | MYSQL_ROOT_PASSWORD: linux101-test
348 | MYSQL_DATABASE: wordpress
349 | MYSQL_ROOT_HOST: "%"
350 |
351 | wordpress:
352 | image: wordpress:latest
353 | container_name: wordpress
354 | restart: always
355 | ports:
356 | - "80:80"
357 | depends_on:
358 | - db
359 | volumes:
360 | - ./wp-content:/var/www/html/wp-content
361 | environment:
362 | WORDPRESS_DB_HOST: db:3306
363 | WORDPRESS_DB_USER: root
364 | WORDPRESS_DB_PASSWORD: linux101-test
365 | ```
366 |
367 | 在文件夹中运行 `docker-compose up` 命令即可启动样例,在 127.0.0.1:80 上即可看到 WordPress 的初始化界面。用 `docker-compose up -d` 命令可以让容器分离(detach)命令行。运行完成后,可以使用 `docker-compose down` 停止并删除容器。
368 |
--------------------------------------------------------------------------------
/docs/Ch08/supplement.md:
--------------------------------------------------------------------------------
1 | ---
2 | icon: material/puzzle
3 | ---
4 |
5 | # 拓展阅读 {#supplement}
6 |
7 | ## 查看容器镜像的工具:Dive {#dive}
8 |
9 | 在调试容器时,我们可能想看一下使用的容器镜像里有哪些文件。[Dive](https://github.com/wagoodman/dive) 是一个很方便的小工具。
10 |
11 | 对于 Debian/Ubuntu 用户,由于其不在官方软件源中,需要手动下载 deb 包安装:
12 |
13 | ```console
14 | $ wget https://github.com/wagoodman/dive/releases/download/v0.9.2/dive_0.9.2_linux_amd64.deb
15 | $ sudo apt install ./dive_0.9.2_linux_amd64.deb
16 | ```
17 |
18 | 之后直接使用即可:
19 |
20 | ```console
21 | $ dive ubuntu:20.04 # 查看 Ubuntu 20.04 镜像中的内容
22 | ```
23 |
24 | ## Docker 以外的容器工具 {#other-container-tools}
25 |
26 | Docker 目前已几乎是容器的代名词了,但是实际上,用户还可以选择其他的程序来运行容器。其中一些代表性的方案有 Podman、LXC 等。
27 |
28 | ### Podman {#podman}
29 |
30 | Podman 是由 Red Hat 编写的容器工具。相比于 Docker,Podman 是无守护进程的(daemonless),不需要像 Docker 一样运行系统服务。并且,Podman 的 CLI 工具与 Docker 的语法兼容(甚至可以设置 `alias docker=podman`),方便用户迁移到 Podman。
31 |
32 | ### LXC {#lxc}
33 |
34 | 相比于 Docker 专注应用容器化而言,LXC(Linux Containers)更加注重完整 Linux 系统的容器虚拟化,用户可以使用 LXC 启动一个近似完整的 Linux 系统。Canonical 公司基于 LXC 编写的 LXD 工具则为管理员提供了一个方便的管理接口。
35 |
36 | ### systemd-nspawn {#systemd-nspawn}
37 |
38 | systemd-nspawn 是非常轻量级的容器,但「麻雀虽小,五脏俱全」。类似于 Unix 环境中历史悠久的 chroot,它能够很方便地从已有的系统文件启动,并且执行需要的系统管理命令。
39 |
40 | ### 自制简单的容器运行时 {#write-a-simple-container-runtime}
41 |
42 | Linux 下的容器本身的运行原理并不复杂。在了解相关概念后,甚至可以自己写一个简单的容器实现。科大 OS(H) 课程 2020 年曾有过编写容器运行时的实验,详情可参考 [OSH 2020 Lab 4](https://osh-2020.github.io/lab-4/)。
43 |
--------------------------------------------------------------------------------
/docs/Ch09/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | icon: material/console
3 | ---
4 |
5 | # Shell 高级文本处理与正则表达式
6 |
7 | !!! Warning "本文已基本完稿,正在审阅和修订中,不是正式版本。"
8 |
9 | !!! abstract "导言"
10 |
11 | 本章节将介绍一些常用的 shell 文本处理工具,它们可以帮助你更加得心应手地处理大量有规律的文本。
12 |
13 | 为保持简洁,各工具的介绍皆点到即止,进一步的用法请自行查找。
14 |
15 | ## 其他 shell 文本处理工具 {#tools-in-shell}
16 |
17 | ### sort {#sort}
18 |
19 | sort 用于文本的行排序。默认排序方式是升序,按每行的字典序排序。
20 |
21 | 一些基本用法:
22 |
23 | - `-r` 降序(从大到小)排序
24 | - `-u` 去除重复行
25 | - `-o [file]` 指定输出文件
26 | - `-n` 用于数值排序,否则“15”会排在“2”前
27 |
28 | ```console
29 | $ echo -e "snake\nfox\nfish\ncat\nfish\ndog" > animals
30 | $ sort animals
31 | cat
32 | dog
33 | fish
34 | fish
35 | fox
36 | snake
37 | $ sort -r animals
38 | snake
39 | fox
40 | fish
41 | fish
42 | dog
43 | cat
44 | $ sort -u animals
45 | cat
46 | dog
47 | fish
48 | fox
49 | snake
50 | $ sort -u animals -o animals
51 | $ cat animals
52 | cat
53 | dog
54 | fish
55 | fox
56 | snake
57 | $ echo -e "1\n2\n15\n3\n4" > numbers
58 | $ sort numbers
59 | 1
60 | 15
61 | 2
62 | 3
63 | 4
64 | $ sort -n numbers
65 | 1
66 | 2
67 | 3
68 | 4
69 | 15
70 | ```
71 |
72 | !!! warning "sort 的结果会受到本地化配置的影响"
73 |
74 | 在使用 sort 时,一个比如容易忽视的问题是当前的本地化配置对结果的影响。
75 |
76 | ```console
77 | $ echo -e 'a b\naa' | LC_ALL=C sort
78 | a b
79 | aa
80 | $ echo -e 'a b\naa' | LC_ALL=en_US.UTF-8 sort
81 | aa
82 | a b
83 | ```
84 |
85 | 为了获得传统意义上逐字节比较的结果,可以指定环境变量 `LC_ALL=C`。
86 |
87 | !!! tip "小知识"
88 |
89 | 为什么有必要存在 `-o` 参数?试试重定向输出到原文件会发生什么吧。
90 |
91 | ### uniq {#uniq}
92 |
93 | uniq 也可以用来排除重复的行,但是仅对连续的重复行生效。
94 |
95 | 通常会和 sort 一起使用:
96 |
97 | ```console
98 | $ sort animals | uniq
99 | ```
100 |
101 | 只是去重排序明明可以用 `sort -u` ,uniq 工具是否多余了呢?实际上 uniq 还有其他用途。
102 |
103 | `uniq -d` 可以用于仅输出重复行:
104 |
105 | ```console
106 | $ sort animals | uniq -d
107 | ```
108 |
109 | `uniq -c` 可以用于统计各行重复次数:
110 |
111 | ```console
112 | $ sort animals | uniq -c
113 | ```
114 |
115 | ## 正则表达式 {#regular-expression}
116 |
117 | 正则表达式(regular expression)描述了一种字符串匹配的模式,可以用来检查一个串是否含有某种子串、将匹配的子串做替换或者从某个串中取出符合某个条件的子串等。
118 |
119 | 此处仅简单介绍正则表达式的一些用法,对正则表达式有更多兴趣,请移步[拓展阅读](./supplement.md)。
120 |
121 | ### 特殊字符 {#special-char}
122 |
123 | | 特殊字符 | 描述 |
124 | | --------------- | ---------------------------------------------------------------------------------------------------------- |
125 | | `[]` | 方括号表达式,表示匹配的字符集合,例如 `[0-9]`、`[abcde]` |
126 | | `()` | 标记子表达式起止位置 |
127 | | `*` | 匹配前面的子表达式零或多次 |
128 | | `+` | 匹配前面的子表达式一或多次 |
129 | | `?` | 匹配前面的子表达式零或一次 |
130 | | `\` | 转义字符,除了常用转义外,还有:`\b` 匹配单词边界;`\B` 匹配非单词边界等 |
131 | | `.` | 匹配除 `\n`(换行)外的任意单个字符 |
132 | | `{}` | 标记限定符表达式的起止。例如 `{n}` 表示匹配前一子表达式 n 次;`{n,}` 匹配至少 n 次;`{n,m}` 匹配 n 至 m 次 |
133 | | \|
| 表明前后两项二选一 |
134 | | `$` | 匹配字符串的结尾 |
135 | | `^` | 匹配字符串的开头,在方括号表达式中表示不接受该方括号表达式中的字符集合 |
136 |
137 | 以上特殊字符,若是想要匹配特殊字符本身,需要在之前加上转义字符 `\`。
138 |
139 | ### 简单示例 {#re-example}
140 |
141 | 匹配正整数:
142 |
143 | ```
144 | [1-9][0-9]*
145 | ```
146 |
147 | 匹配仅由 26 个英文字母组成的字符串:
148 |
149 | ```
150 | ^[A-Za-z]+$
151 | ```
152 |
153 | 匹配 Chapter 1-99 或 Section 1-99
154 |
155 | ```
156 | ^(Chapter|Section) [1-9][0-9]{0,1}$
157 | ```
158 |
159 | 匹配“ter”结尾的单词:
160 |
161 | ```
162 | ter\b
163 | ```
164 |
165 | ### 基本/扩展正则表达式 {#bre-ere}
166 |
167 | 基本正则表达式(Basic Regular Expressions, BRE)和扩展正则表达式(Extended Regular Expressions, ERE)是两种 POSIX 正则表达式风格。
168 |
169 | BRE 可能是如今最老的正则风格了,对于部分特殊字符(如 `+`, `?`, `|`, `{`)需要加上转义符 `\` 才能表达其特殊含义。
170 |
171 | ERE 与如今的现代正则风格较为一致,相比 BRE,上述特殊字符默认发挥特殊作用,加上 `\` 之后表达普通含义。
172 |
173 | 具体的例子在下文介绍工具时可以看到。
174 |
175 | !!! tip "帮助理解正则表达式的工具"
176 |
177 | [Regex101](https://regex101.com/) 网站集成了常见编程语言正则表达式的解析工具,在编写正则时可以作为一个不错的参考。
178 |
179 | ## 常用 Shell 文本处理工具(正则) {#tools-with-re}
180 |
181 | ### grep {#grep}
182 |
183 | grep 全称 Global Regular Expression Print,是一个强大的文本搜索工具,可以在一个或多个文件中搜索指定 pattern 并显示相关行。
184 |
185 | grep 默认使用 BRE,要使用 ERE 可以使用 `grep -E` 或 egrep。
186 |
187 | 命令格式:`grep [option] pattern file`
188 |
189 | 一些用法:
190 |
191 | - `-n`:显示匹配到内容的行号
192 | - `-v`:显示不被匹配到的行
193 | - `-i`:忽略字符大小写
194 |
195 | ```console
196 | $ ls /bin | grep -n "^man$" # 搜索内容仅含 man 的行,并且显示行号
197 | $ ls /bin | grep -v "[a-z]\|[0-9]" # 搜索不含小写字母和数字的行
198 | $ ls /bin | grep -iv "[A-Z]\|[0-9]" # 搜索不含字母和数字的行
199 | ```
200 |
201 | ### sed {#sed}
202 |
203 | sed 全称 Stream EDitor,即流编辑器,可以方便地对文件的内容进行逐行处理。
204 |
205 | sed 默认使用 BRE,要使用 ERE 可以 sed -E。
206 |
207 | 命令格式:
208 |
209 | ```console
210 | $ sed [OPTIONS] 'command' file(s)
211 | $ sed [OPTIONS] -f scriptfile file(s)
212 | ```
213 |
214 | 此处的 command 和 scriptfile 中的命令均指的是 sed 命令。
215 |
216 | 常见 sed 命令:
217 |
218 | - s 替换
219 | - d 删除
220 | - c 选定行改成新文本
221 | - a 当前行下插入文本
222 | - i 当前行上插入文本
223 |
224 | ```console
225 | $ echo -e "seD\nIS\ngOod" > sed_demo
226 | $ cat sed_demo
227 | seD
228 | IS
229 | gOod
230 | $ sed "2d" sed_demo # 删除第二行
231 | seD
232 | gOod
233 | $ sed "s/[a-z]/~/g" sed_demo # 替换所有小写字母为 ~
234 | ~~D
235 | IS
236 | ~O~~
237 | $ sed "3cpErfeCt" sed_demo # 选定第三行,改成 pErfeCt
238 | seD
239 | IS
240 | pErfeCt
241 | ```
242 |
243 | ### awk {#awk}
244 |
245 | awk 是一种用于处理文本的编程语言工具,名字来源于三个作者的首字母。相比 sed,awk 可以在逐行处理的基础上,针对列进行处理。默认的列分隔符号是空格,其他分隔符可以自行指定。
246 |
247 | awk 使用 ERE。
248 |
249 | 命令格式:`awk [options] 'pattern {action}' [file]`
250 |
251 | awk 逐行处理文本,对符合的 patthern 执行 action。需要注意的是,awk 使用单引号时可以直接用 `$`,使用双引号则要用 `\$`。
252 |
253 | 一些示例:
254 |
255 | ```console
256 | $ cat awk_demo
257 | Beth 4.00 0
258 | Dan 3.75 0
259 | kathy 4.00 10
260 | Mark 5.00 20
261 | Mary 5.50 22
262 | Susie 4.25 18
263 | $ # 选择第三列值大于 0 的行,对每一行输出第一列的值和第二第三列的乘积
264 | $ awk '$3 >0 { print $1, $2 * $3 }' awk_demo
265 | kathy 40
266 | Mark 100
267 | Mary 121
268 | Susie 76.5
269 | ```
270 |
271 | 示例中 `$1`,`$2`,`$3` 分别指代本行的第 1、2、3 列。特别地,$0 指代本行。
272 |
273 | awk 语言是「图灵完全」的,这意味着理论上它可以做到和其他语言一样的事情。这里我们不仅可以对每行进行操作,还可以定义变量,将前面处理的状态保存下来,以下是一个求和的例子:
274 |
275 | ```console
276 | $ awk 'BEGIN { sum = 0 } { sum += $2 * $3 } END { print sum }' awk_demo
277 | 337.5
278 | ```
279 |
280 | 关于 awk,有一本知名的书籍《The AWK Programming Language》([中文翻译](https://github.com/wuzhouhui/awk)),感兴趣的读者可以考虑阅读。
281 |
282 | ## 思考题 {#questions}
283 |
284 | !!! question "正则表达式引擎"
285 |
286 | 什么是 DFA/NFA 正则表达式引擎?如今常见编程语言里的正则表达式实现和此处的 BRE/ERE 有什么异同?
287 |
288 | !!! question "正则表达式练习 1:邮件标题匹配"
289 |
290 | 最近你收到了很多垃圾邮件,而且垃圾邮件检测似乎没有生效。你发现这些垃圾邮件的标题似乎都满足一个正则表达式。这些垃圾邮件的标题类似如下:
291 |
292 | - `162935832----系统通知`
293 | - `166819038----系统警告`
294 |
295 | 请写出能够匹配类似标题的正则表达式。
296 |
297 | 此外,作为一个负责任的系统管理员,你订阅了 Debian Security 邮件列表(订阅后能够收到 Debian 安全更新的通知和说明邮件)。但是你发现,你的邮件系统真是成事不足败事有余,似乎喜欢把这些邮件放进垃圾邮件箱,要是漏掉了什么重要的安全更新就麻烦了!Debian Security 发送的邮件标题类似如下:
298 |
299 | - `[SECURITY] [DSA 5075-1] minetest security update`
300 | - `[SECURITY] [DSA 5059-1] policykit-1 security update`
301 | - `[SECURITY] [DSA 5086-1] thunderbird security update`
302 |
303 | 同样,请写出能够匹配类似标题的正则表达式。
304 |
305 | !!! question "正则表达式练习 2:弹幕过滤"
306 |
307 | 某弹幕视频网站支持使用正则表达式过滤不想看到的弹幕。某日忍无可忍之下,你希望编写一条正则表达式过滤掉类似如下的弹幕(其中全角省略号为任意文本):
308 |
309 | - `当年我就是听的这首歌才……`
310 | - `我就是听的这首歌帮……`
311 | - `当年爷……时就是听的这首歌`
312 | - `当年那天晚上要不是放这首歌我就被……`
313 |
314 | 可以接受少许的误过滤(false positive)。
315 |
316 | !!! question "正则表达式练习 3:Vscode 中的文本批量替换"
317 |
318 | Vscode 支持使用正则表达式语法查找与替换文本内容。有一天,你的项目中使用的某个函数更新了:调用方式从 `func1(a, b, c)` 变成了 `func1_new(c, b, a, null)`。其中假设 `a`、`b`、`c` 均为不含逗号的表达式。
319 |
320 | 尝试写出搜索的正则表达式与替换目标表达式。提示:正则表达式中使用 `()` 包裹的为一组,在 vscode 的替换目标表达式中可以使用 `$1`、`$2` 的格式来引用第一组、第二组等内容。
321 |
322 | !!! question "Shell 文本处理工具练习 1:文件内容替换"
323 |
324 | 某 shell 脚本会随着图形界面启动而启动,启动后会根据环境变量替换某程序配置文件的内容。该配置文件内容如下:
325 |
326 | ```ini
327 | [settings]
328 | homepage=http://example.com/
329 | location-entry-search=http://cn.bing.com/search?q=%s
330 | ```
331 |
332 | 我们希望编写一条或多条 sed 命令,使得脚本运行后配置文件被修改为:
333 |
334 | ```ini
335 | [settings]
336 | homepage=http://example.com/index_new.html
337 | location-entry-search=http://www.wolframalpha.com/input/?i=%s
338 | ```
339 |
340 | 假设配置文件路径存储在变量 `$F` 中。请注意:图形界面可能会重置,此时脚本会对已经修改的配置文件再次修改,如果编写不小心,可能会得到错误的结果。
341 |
342 | !!! question "Shell 文本处理工具练习 2:Nginx 日志分析"
343 |
344 | 你的网站最近收到了一大批不正常的请求大量消耗服务器带宽,你希望通过 shell 文本处理工具确认攻击者的来源 IP。Nginx 访问日志的格式类似于如下:
345 |
346 | ```
347 | 123.45.67.89 - - [01/Mar/2022:00:58:17 +0800] "GET /downloads/nonexist HTTP/1.1" 404 190 "-" "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2226.0 Safari/537.36"
348 | ```
349 |
350 | 其中我们主要关注 IP(第一列)和下载大小(第 10 列,例子中为 190)。请给出使用 `awk`、`sort` 等工具输出下载量最大的前 50 个 IP 的命令。
351 |
352 | !!! question "Shell 文本处理工具练习 3:文件列表解析"
353 |
354 | Ports 是 BSD 系列操作系统管理编译软件的方式。下面我们将介绍 FreeBSD 操作系统中的 ports 目录结构。
355 |
356 | Ports 目录的第一层为不同软件的分类(诸如音频程序、数据库程序会分别放置在 audio 和 databases 目录下),第二层则为各个软件的目录。在绝大多数软件的目录下都有 `distinfo` 文件,用于描述其依赖的源代码包文件的名称、大小和 SHA256 校验值信息。
357 |
358 | 例如,`gcc10` 软件包的 `distinfo` 位于 `lang/gcc10/distinfo`,内容类似如下:
359 |
360 | ```
361 | TIMESTAMP = 1619249722
362 | SHA256 (gcc-10.3.0.tar.xz) = 64f404c1a650f27fc33da242e1f2df54952e3963a49e06e73f6940f3223ac344
363 | SIZE (gcc-10.3.0.tar.xz) = 76692288
364 | ```
365 |
366 | 你的任务是:搜索 ports 中的所有 distinfo,提取所有文件名和 SHA256,按照文件名以字典序排序并输出,每行格式要求如下:
367 |
368 | ```
369 | 64f404c1a650f27fc33da242e1f2df54952e3963a49e06e73f6940f3223ac344 gcc-10.3.0.tar.xz
370 | ```
371 |
372 | 现实中的 ports 文件可以从 下载解压得到。
373 |
374 | 注意:少量 `distinfo` 文件的 SHA256 对应行最后会有多余的空格或制表符,需要妥善处理。
375 |
--------------------------------------------------------------------------------
/docs/Ch09/supplement.md:
--------------------------------------------------------------------------------
1 | ---
2 | icon: material/puzzle
3 | ---
4 |
5 | # 拓展阅读 {#supplement}
6 |
7 | ## 正则 {#regex}
8 |
9 | 注:以下正则语法如需在 `grep` 中使用,需要加上参数 `-P`,表示使用 Perl 语法。
10 |
11 | ### 懒惰和贪婪 {#lazy-and-greedy}
12 |
13 | 使用 `*` `+` 的时候默认是贪婪模式,即尽可能匹配更多的子表达式。在 `*` `+` 之后加上 `?` 变为懒惰模式,即尽可能匹配更少的子表达式。
14 |
15 | 例如:`123456456`
16 |
17 | 贪婪:`1.+6` -> `123456456`
18 |
19 | 懒惰:`1.+?6` -> `123456`
20 |
21 | ### 后向引用 {#backreference}
22 |
23 | 后向引用可以将之前匹配到的具体内容再次利用。在正则表达式中,`()` 以及它们包含的内容为一个分组,每个分组默认拥有一个组号。
24 |
25 | 组号分配规则:
26 |
27 | - 0 代表整个表达式
28 | - 从左至右,按左括号的出现顺序分配,第一个为 1,第二个为 2,以此类推
29 | - 扫描两遍,第一次只分配未命名的组,第二次只分配命名的组。即任意命名组的组号都大于未命名的组号
30 |
31 | !!! example "示例"
32 |
33 | `\b(\w+)\b\s+\1\b` 可以用来匹配用空白字符分割的两个重复的单词(例如 `linux linux`),其中 `\1` 是对组号为 1 的组 `(\w+)` 的引用。
34 |
35 | `\b(?'Word'\w+)\b\s+\k'Word'\b` 也可以起到同样的效果,其中 `?'Word'` 用于命名组,`\k'Word'` 用于引用组。
36 |
37 | ### 零宽断言 {#lookaround}
38 |
39 | 零宽断言用于查找某些内容进行定位,但内容并不放入匹配结果,就像 `\b` `^` `$` 的定位一样。`(?=exp)` 用于匹配表达式 `exp` 前面的位置,`(?<=exp)` 用于匹配后面的位置。
40 |
41 | !!! example "示例"
42 |
43 | `\b\w+(?=ing\b)` 可以匹配 `ing` 结尾的单词前面的部分。例如 `going` 中的 `go` 会被该正则匹配。
44 |
45 | `(?<=\bre)\w+\b` 可以匹配 `re` 开开头的单词的后面一部分。例如 `revue` 中的 `vue` 会被该正则匹配。
46 |
47 | ### 正则表达式的代价 {#ddos}
48 |
49 | 为了支持复杂语法,现在的正则表达式引擎一般使用回溯搜索的方法来进行匹配。对于某些特定的正则,攻击者可以构造出能够让其执行很长时间的字符串,从而导致系统崩溃。
50 |
51 | 相关介绍:[乱用正则引发的惨案](https://zhuanlan.zhihu.com/p/46294360)
52 |
--------------------------------------------------------------------------------
/docs/Ch10/images/ms-store-search-linux.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/Ch10/images/ms-store-search-linux.png
--------------------------------------------------------------------------------
/docs/Ch10/index.md:
--------------------------------------------------------------------------------
1 | # Windows Subsystem for Linux
2 |
3 | !!! Warning "本文仍在编辑中,尚未校对、审阅和和修订"
4 |
5 | !!! abstract "导言"
6 |
7 | 本章节将介绍在 Windows 上体验和使用 Linux 的较好的方法之一——Windows Subsystem for Linux。
8 |
9 | WSL 有着接近原生的性能、GPU 的支持和极易的安装方法,完整的 Linux 内核环境可以在涵盖绝大部分的 Linux 日常开发环境。
10 |
11 | ## 安装 {#install}
12 |
13 | ### 自动安装方法 {#install-auto}
14 |
15 | 在 Windows 10 Build 19041(Windows 10 2004)版本或以上,可以通过一条命令直接完成安装(需要以管理员身份运行):
16 |
17 | ```shell
18 | wsl --install
19 | ```
20 |
21 | 这条命令会完成如下几个操作:
22 |
23 | - 启用 WSL 和虚拟化平台
24 | - 下载并安装 WSL 的 Linux 内核
25 | - 设定 WSL 2 默认的版本
26 | - 从微软商店里面下载 Ubuntu 进行安装
27 |
28 | 安装完毕之后可以直接通过 `ubuntu` 命令启动。
29 |
30 | ### 手动安装 {#install-manual}
31 |
32 | 如果系统版本在 Windows 10 Build 19041 以下,如 Windows 10 LTSC 2019 ,需要手动配置系统。
33 |
34 | #### 启用 WSL 和虚拟化平台 {#enable-windows-features}
35 |
36 | 使用管理员身份在 Powershell 中运行
37 |
38 | ```batch
39 | Dism.exe /Online /Enable-feature /Featurename:Microsoft-Windows-Subsystem-Linux /All /NoRestart
40 | Dism.exe /Online /Enable-feature /Featurename:VirtualMachinePlatform /All /NoRestart
41 | ```
42 |
43 | #### 安装 Linux 内核 {#install-linux-kernel}
44 |
45 | 从微软下载 Linux 内核用于 Windows 的更新,并安装
46 |
47 | [x64 内核](https://wslstorestorage.blob.core.windows.net/wslblob/wsl_update_x64.msi)
48 |
49 | [ARM64 内核](https://wslstorestorage.blob.core.windows.net/wslblob/wsl_update_arm64.msi)
50 |
51 | #### 设置 WSL 2 为默认的 WSL 版本 {#set-default-wsl-version}
52 |
53 | ```powershell
54 | wsl --set-default-version 2
55 | ```
56 |
57 | ## 安装发行版 {#install-distro}
58 |
59 | 有许多的方法安装一个新的发行版。
60 |
61 | ### 从微软商店下载 {#install-distro-msstore}
62 |
63 | 在微软商店中直接搜索对应的发行版进行安装即可,这种方法可以安装的发行版并不多,如 Arch Linux 和 Fedora 就无法通过这种方法进行安装。
64 |
65 | 
66 |
67 | ### 使用 Appx 文件进行安装 {#install-distro-appx}
68 |
69 | 有的打包为 Appx 的分发方式,也有提供了可执行应用程序来进行安装,在仓库里面提供了安装方式
70 |
71 | - ArchLinux: [ArchWSL](https://github.com/yuk7/ArchWSL)
72 | - Fedora: [FedoraWSL](https://github.com/yosukes-dev/FedoraWSL)
73 | - Manjaro: [ManjaroWSL](https://github.com/sileshn/ManjaroWSL)
74 | - Gentoo: [GentooWSL](https://github.com/imaandrew/GentooWSL)
75 |
76 | ### 使用 LxRunOffline 进行安装 {#install-lxrunoffline}
77 |
78 | LxRunOffline 是进行 WSL 管理的一个应用程序,可以帮助管理 WSL 的安装与配置
79 |
80 | LxRunOffline 仓库:[LxRunOffline](https://github.com/DDoSolitary/LxRunOffline)
81 |
82 | LxRunOffline 是通过 Windows 提供的 API 来进行管理 WSL 的一个命令行工具
83 |
84 | #### 例:安装 Arch Linux {#install-lxrunoffline-archlinux}
85 |
86 | 可以从 [LxRunOffline/wiki](https://github.com/DDoSolitary/LxRunOffline/wiki) 里面下载需要的发行版的 rootfs
87 |
88 | 对于 ArchLinux,可以从[这个链接](https://lxrunoffline.apphb.com/download/ArchLinux),下载 ArchLinux 的 rootfs 得到 `archlinux-bootstrap-*.tar.gz`
89 |
90 | 使用 LxRunOffline 进行安装
91 |
92 | ```shell
93 | ./LxRunOffline i -n ArchLinux -d D:/wsl/ArchLinux -f ./archlinux-bootstrap-*.tar.gz
94 | ```
95 |
96 | - `-n` WSL 的名称
97 |
98 | - `-d` 希望安装的位置,通过这种方法可以不需要将 wsl 安装在 C 盘
99 |
100 | 需要注意的是,这个安装方法不会在开始菜单中添加启动快捷方式,只能够通过命令行的方式启动:
101 |
102 | ```powershell
103 | wsl -d ArchLinux
104 | ```
105 |
106 | 在安装完成后打开,默认是以 root 的身份执行,如果需要默认以其他的用户的身份运行,可以在添加了用户后,使用 LxRunOffline 运行:
107 |
108 | ```shell
109 | ./LxRunOffline su -n ArchLinux -v 1000
110 | ```
111 |
112 | 其中:
113 |
114 | - `ArchLinux` 是 WSL 的名
115 | - `-v 1000` 是设置登录的用户的 UID
116 |
117 | ## 使用 {#usage}
118 |
119 | ### VS Code {#vscode-remote-wsl}
120 |
121 | 在 VS Code 中安装插件 "Remote - WSL" 后,可以像 "Remote - SSH" 一样使用 WSL
122 |
123 | 
124 |
125 | ### JetBrains {#jetbrains-remote-wsl}
126 |
127 | 包括 IntelliJ IDEA、PyCharm、WebStorm 等,都有对 WSL 的支持。在打开了 WSL 下的目录后,
128 | 会自动地选择 WSL 下的工具链,Git 会使用 WSL 中安装的 Git 而不是 Window 下的 Git。
129 |
130 | 但是还有一些问题仍待解决,如 PyCharm 并不支持 WSL 中的虚拟环境,在添加 Python Inpterpreter 时,
131 | 无法检测并使用现有的如 `virtualenv`、`conda` 等创建的虚拟环境,也无法创建。
132 |
133 | 对应的的 Issue:[PY-32853](https://youtrack.jetbrains.com/issue/PY-32853)
134 |
135 | 可以 workaround 来解决:在添加 Python Inpterpreter 时,添加 System Python Inpterpreter 而不是
136 | Virtual Environment,在路径中填写虚拟环境下的 Python 路径,PyCharm 可以正确识别安装的包,但是在打开
137 | 终端的时候,不会自动地 `source` 虚拟环境。
138 |
139 | ### 在 WSL 中使用资源管理器打开文件夹 {#open-folder-in-wsl}
140 |
141 | 在 WSL 的终端中,直接输入 `explorer.exe .`,会在当前目录下打开资源管理器,也可以使用 Windows 上的应用程序打开相应的文件。
142 |
143 | ## 已知的问题 {#known-issues}
144 |
145 | ### 运行 32 位应用程序 {#32-bit-exe}
146 |
147 | 由于实现原理,WSL 1 并不支持运行 32 位应用程序。WSL 2 由于是完整的 Linux 内核,可以在安装了对应的
148 | 运行时后可以运行 32 位应用程序。
149 |
150 | ### WSL 下 `/mnt/` 性能问题 {#mnt-performance}
151 |
152 | WSL 2 由于相当于是通过网络的方式访问 `/mnt/` 下的文件,导致性能相对于 WSL 1 有明显的下降。
153 |
154 | 可以通过将文件放在 `/` 下(不包括 `/mnt/`),来提高性能。
155 |
156 | 相关的 Issue 和评论:[microsoft/WSL#4197](https://github.com/microsoft/WSL/issues/4197#issuecomment-604592340)
157 |
158 | ### systemd {#systemd}
159 |
160 | 无论是 WSL 1 还是 WSL 2,都不原生支持 systemd,如果需要 systemd,可以通过 [systemd-genie](https://github.com/arkane-systems/genie) 来实现。
161 |
--------------------------------------------------------------------------------
/docs/Spec/slide.md:
--------------------------------------------------------------------------------
1 | # 演示文稿规格
2 |
3 | !!! warning "本文面向编写组提供指导和规范,不面向读者阅读,不属于本书正文。"
4 |
5 | ## 1. 文件格式
6 |
7 | #### (a) 演示文稿的文件格式应属于如下列出的一种或多种:
8 |
9 | - (1) Adobe 可移植文档格式,默认扩展名为 `.pdf`;
10 |
11 | - (2) Microsoft PowerPoint Open XML 演示文件格式,默认扩展名为 `.pptx`;
12 |
13 | - (3) Prezi 文件格式,默认扩展名为 `.pez`。
14 |
15 | #### (b) 相同内容的演示文稿可以存在多个文件格式,不过必须包括 §1(a)(1)。
16 |
17 | ## 2. 内容
18 |
19 | #### (a) 一份演示文稿应当只用来描述一章的知识而不跨越章节。
20 |
21 | #### (b) 演示文稿的内容推荐包括的内容有:
22 |
23 | - (1) 本章的标题;
24 |
25 | - (2) 演示文稿所涉及内容的目录结构;
26 |
27 | - (3) 本章正文中的所有非自主阅读节的核心内容;
28 |
29 | - (4) 正文中的提示框内容;
30 |
31 | - (5) 正文中未出现,但与 §2(b)(2) 相关的背景知识、实例演示等;
32 |
33 | - (6) 结束页面。
34 |
35 | #### (c) §2(b)(1 - 3, 6) 是必需的,其余部分可以按演讲时长、风格等需求考虑添加。
36 |
37 | #### (d) 演示文稿的内容不推荐包括:
38 |
39 | - (1) 本章的自主阅读节;
40 |
41 | - (2) 本章的拓展阅读;
42 |
43 | - (3) 附录,但是特别相关的除外;
44 |
45 | - (4) 与正文内容无关的其它内容。
46 |
47 | #### (e) 演示文稿的内容应当按照本章正文的编写顺序组织,逻辑应当连贯,不过无需面面俱到。
48 |
49 | ## 3. 排版
50 |
51 | #### (a) §2(b)(1) 建议使用 1 页,且放在最开头,其中推荐包括的内容为:
52 |
53 | - (1) 本章的标题名、章序号;
54 |
55 | - (2) “中国科学技术大学 LUG” 字样;
56 |
57 | - (3) 演讲的日期;
58 |
59 | - (4) 演讲者姓名或昵称;
60 |
61 | - (5) 名人引言,或者对本章的精要概括。
62 |
63 | #### (b) §3(a)(1 - 3) 是必需的。
64 |
65 | #### (c) §2(b)(2) 紧跟,建议使用 1 ~ 2 页,按顺序列出 h2 节标题即可。
66 |
67 | #### (d) 之后,每一 h2 节都以一页节标题分割,随后包含若干页内容用于解说,这些内容包括 §2(b)(3 - 5) 中指定的内容,且其文字部分的字号都应不小于 28。
68 |
69 | #### (e) §2(b)(6) 放置在最后,应包括简短的致谢词语。
70 |
71 | ## 4. 制作与发布
72 |
73 | #### (a) 一章的演示文稿应由本章的主要负责人来进行编写,编写的内容由主要负责人自定,但应符合“演示文稿规格”指定的基本要求,以确保格式大体一致。
74 |
75 | #### (b) 演示文稿应在正式演讲前的 12 小时之前向编写组提出第一版,由所有成员参与,在接下来的 10 小时内审阅和修订。
76 |
77 | #### (c) 对演示文稿中较大的结构和内容调整应当在 §4(b) 的阶段内完成,§4(b) 之后至正式演讲前可以做微小的改动。
78 |
79 | #### (d) 演讲开始后,演示文稿的 §1(a)(1) 版应上传到指定的在线地址。
80 |
--------------------------------------------------------------------------------
/docs/Spec/writing.md:
--------------------------------------------------------------------------------
1 | # 章节编写指导(标题请用 h1 等级编写。)
2 |
3 | !!! warning "本文面向编写组提供指导和规范,不面向读者阅读,不属于本书正文。"
4 |
5 | !!! abstract "导言"
6 |
7 | 在每一章的开始都需要编写章节导言。导言的目的有两个:一是为了铺垫一些前置知识以方便后续展开正文;二是写出一个内容摘要来辅助读者和编者自己快速了解该章节的核心内容和脉络。编写导言时可以自己组织语言,以简练为主,不需要面面俱到。
8 |
9 | “章节编写指导”是一份写给该讲义的创作者所用的参考教程。在接下来的编写中,推荐在本地也安装一个 MkDocs 来实时预览项目。MkDocs 基于 Python,故可以使用诸如 `pip install mkdocs` 等命令完成安装。更全面的安装流程请参考 [MkDocs 官网安装说明](https://www.mkdocs.org/#installation) 实现。
10 |
11 | 使用形如 `!!! abstract "导言"` 的方式添加一个导言框,并在下面若干行通过缩进 1 个制表符或 4 个空格的方式填写导言里的内容,行与行之间请空 1 行。
12 |
13 | !!! info "Markdown 格式注意事项"
14 |
15 | 目前 CI (GitHub Actions) 在部署文档时,额外添加了使用 [Prettier](https://prettier.io) 检查 Markdown 风格的步骤,如果不符合要求,你的修改会被拒绝。请在编辑完成后使用 Prettier 检查并修复 Markdown 格式。
16 |
17 | 可以在仓库根目录使用 `npm install` 安装 Prettier,使用 `npm run check` 检查文件格式,使用 `npm run fix` 修复。
18 |
19 | ## 章节主体(主体内容请从 h2 等级以下按层次编写。) {#main-content}
20 |
21 | 章节里的主要内容都应该写在主体里。主体包括标题和正文:标题都从 h2 等级以下按层次编写,而正文则直接使用普通文本即可。
22 |
23 | 主体里应当包括与该章节主题相关的详细内容,具体内容依赖于课纲。建议每个 h2 等级的标题都包含一个完整的子模块,不同的 h2 子模块的内容尽可能没有强烈的依赖。这个标准同样适用于 h3 及以下的子段落。
24 |
25 | 每一段主体应当有完整的内容、正确的逻辑和通顺的文字。请尽力避免诸如知识点依赖链缺失、逻辑错误和文笔零碎等影响读者阅读体验的问题。建议每次写完以后通过想象自己正是读者进行阅读的方式来查漏补缺,也可以通过同行交叉审阅的方式获取宝贵的建议。
26 |
27 | ### 在本地随时预览当前主题下的格式 {#local-preview}
28 |
29 | 目前当前的主题已经确定为 Material,可以使用诸如 `pip install mkdocs-material` 等命令完成主题的安装,并在工作根目录下使用 `mkdocs serve` 命令并访问 来实时预览,这对讲义的编写十分有帮助。
30 |
31 | 更全面的安装和配置信息请参考 [MkDocs 官网](https://www.mkdocs.org) 和 [Material for MkDocs 官网](https://squidfunk.github.io/mkdocs-material/)。
32 |
33 | ### 善用提示框让正文主次分明 {#use-admonitions}
34 |
35 | 通常来说,主体要包含的内容如果需要写得很详尽,不免会带来主次不分的问题。因为很多知识点的结构很接近有向无环图,而文字毕竟都是线性的。非要说使用拓扑排序虽然可以保证不会出现知识点依赖编写颠倒的问题,但也难以让读者快速分析出主干和枝节。
36 |
37 | 请善用提示框,让读者对内容的主次、成分一目了然,也能让你的作品层次更加丰富。
38 |
39 | !!! info "重点"
40 |
41 | 建议用这种提示框来划出重要的知识点,可以是一段内容的核心总结。
42 |
43 | 使用形如 `!!! info "重点"` 的方式添加一个重点框。
44 |
45 | !!! example "范例"
46 |
47 | 建议用这种提示框来列出一个范例。
48 |
49 | 使用形如 `!!! example "范例"` 的方式添加一个范例框。
50 |
51 | !!! tip "小知识"
52 |
53 | 建议用这种提示框来在保留正文连贯性的同时添加细枝末节的知识。
54 |
55 | 使用形如 `!!! tip "小知识"` 的方式添加一个小知识框。
56 |
57 | **注意:请勿拼写为 tips,否则格式会被识别为提示(note)框。**
58 |
59 | !!! warning "请在提示框的标题行后面留一个空行"
60 |
61 | 由于 Prettier 的解析方式问题,请在所有提示框的起始行后面添加一个空行,**不要像 Material 主题官网那样没有空行直接开始提示框内容**。
62 |
63 | :fontawesome-solid-circle-xmark:{: .orangered } **错误**格式:
64 |
65 | ```markdown
66 | !!! note
67 | 提示框内容
68 | ```
69 |
70 | :fontawesome-solid-circle-check:{: .limegreen } **正确**格式:
71 |
72 | ```markdown
73 | !!! note
74 |
75 | 提示框内容
76 | ```
77 |
78 | 更多种类的提示框请参考 [提示框一览](https://squidfunk.github.io/mkdocs-material/reference/admonitions/)。
79 |
80 | ### 为标题和小标题添加 ID {#heading-ids}
81 |
82 | 由于文章篇目较长,使用时会经常遇到需要链接到文章某一段的情况。受限于 MkDocs 自动生成 Anchor ID 的功能(只支持英文字符),纯中文的标题会导致生成 `_1`, `_2` 这样的 ID。一方面这样的 ID 看起来不直观,另一方面每当标题发生增减时这些 ID 都会变,因此请为每个标题手动添加一个有意义的 ID(最开始的标题 H1 除外),方法如下:
83 |
84 | ```markdown
85 | ### 为标题和小标题添加 ID {#heading-ids}
86 | ```
87 |
88 | 建议 ID 只包含小写字母、数字和横线 `-`,必要时使用句点(不使用大写字母和其他标点符号)。
89 |
90 | !!! warning "注意"
91 |
92 | `{#` **前面**需要有一个空格,否则你会像下面这位同学一样翻车:
93 |
94 | 
95 |
96 | 出于风格一致性考虑,请不要在 `{#` **后面**加空格:
97 |
98 | ```markdown
99 | ✔ ### 为标题和小标题添加 ID {#heading-ids}
100 | ❌ ### 为标题和小标题添加 ID {# heading-ids}
101 | ```
102 |
103 | !!! warning "注意 2"
104 |
105 | 请不要在每页最开始的标题(唯一一个 H1)后添加 `{#id-tag}`,否则可能会出现一些意料之外的显示错误。
106 |
107 | ### 为图片添加配字 {#image-caption}
108 |
109 | 在图片下方写一行文字作为配字,并在这行字**紧接着的下一行**(不能有空行)写上 `{: .caption }`,这样配的这行字渲染成 HTML 时就加上了 `class="caption"`,显示为 0.94 倍的字体、灰色、贴近图片。
110 |
111 | ```markdown
112 | 
113 |
114 | 图 1. 这张图片的一行配字
115 | {: .caption }
116 | ```
117 |
118 | ## 自主阅读节 \* {#extra-reading}
119 |
120 | 可以在章节主体中包括若干自主阅读节,原则上这一部分的结构与其它章节主体并无不同,只是不会在课堂上讲授。
121 |
122 | 在所有自主阅读节的标题后面打上星号 \* ,并建议尽可能放在后面。
123 |
124 | ## 思考题 {#thinking-question}
125 |
126 | 建议在每一章后设计若干思考题,来帮助读者投入到一些实际问题的思考中。好的思考题推荐从实际需求中采集灵感,并且拥有简单的题干和典型的解决思路。
127 |
128 | !!! question "如何得到代表思考题的提示框?"
129 |
130 | 建议使用这样的提示框可以用来表达一个思考题。
131 |
132 | 因此使用什么样的命令能生成这样的提示框呢?(请参考源码,或者上文给出的提示框一览链接。)
133 |
134 | 建议不要直接把答案放在每个问题下方,可以专门编写一份思考题解答页面集中放置。
135 |
136 | ## 拓展阅读 {#further-reading}
137 |
138 | Linux 的知识结构呈非线性,仅有单线的正文是不足的。请广泛查阅与本章相关的资料,根据实际需要为读者适当规划一些与本章相关的额外知识,并随附优质的教程、百科等资源(如有),供感兴趣的读者进一步阅读。拓展阅读放置在与正文平级的 `supplement.md` 中,每个独立的额外知识点都是一个 h2 等级节。
139 |
140 | !!! warning "额外前置知识警告"
141 |
142 | 为了保证编写思路不受限,以及鼓励读者多多自行学习,拓展阅读可以依赖后续章节和本书规划内容以外的知识。如果存在这种情况,请在对应的节标题下面紧跟一个警告提示框,指出所依赖的知识(书内或书外)。警告形式如下:
143 |
144 | 本节拓展内容依赖如下额外的前置知识,建议先阅读并掌握对应内容后再研读本节:
145 |
146 | * [第一章 中科大开源社群:LUG@USTC](../Ch01/index.md)
147 | * [LUG@USTC 官方网站](https://lug.ustc.edu.cn/)
148 |
149 | ## 引用与脚注 {#reference-footnote}
150 |
151 | 脚注用于在正文尾部注明一小段内容的的来源引用链接或者是进行**不重要**的说明。[^1]因为重要的说明最好直接跟在后面解释或者在段落后面用提示框,以免破坏读者阅读的连贯性。
152 |
153 | 引用框则用于在正文和拓展内容中引用他人的言论或指出外链。
154 |
155 | !!! quote "中文文案排版规范"
156 |
157 | 请参考 [该规范](https://github.com/sparanoid/chinese-copywriting-guidelines/blob/master/README.zh-Hans.md) 来统一最基本的中英文排版格式。
158 |
159 | 使用形如 `!!! quote "小知识"` 的方式添加一个引用框。
160 |
161 | [^1]: 不重要的说明如:某些名词的来历、解释(缩写的)术语、一些题外轶事和插曲等。
162 |
--------------------------------------------------------------------------------
/docs/credits.md:
--------------------------------------------------------------------------------
1 | # 功劳簿
2 |
3 | ## 主要贡献者 {#team}
4 |
5 | 项目主策划:[RiessarSadyx](https://github.com/RiessarSadyx)
6 |
7 | 各章主要作者:
8 |
9 | - 第一章:[RiessarSadyx](https://github.com/RiessarSadyx)
10 | - 第二章:[LamWS](https://github.com/LamWS)
11 | - 第三章:[zeyugao](https://github.com/zeyugao)
12 | - 第四章:[psi-cmd](https://github.com/psi-cmd)
13 | - 第五章:[taoky](https://github.com/taoky)
14 | - 第六章:[yuanyiwei](https://github.com/yuanyiwei)
15 | - 第七章:[myl7](https://github.com/myl7)
16 | - 第八章:[taoky](https://github.com/taoky)
17 | - 第九章:[RubyOcelot](https://github.com/RubyOcelot)
18 |
19 | 编排与校对:[iBug](https://github.com/iBug),[taoky](https://github.com/taoky)
20 |
21 | ## 贡献者 {#contributors}
22 |
23 | [](https://github.com/ustclug/Linux101-docs/graphs/contributors)
24 |
--------------------------------------------------------------------------------
/docs/css/extra.css:
--------------------------------------------------------------------------------
1 | .red, .red-important { color: red !important; }
2 |
3 | .darkred, .darkred-important { color: darkred !important; }
4 |
5 | .orangered, .orangered-important { color: orangered !important; }
6 |
7 | .green, .green-important { color: green !important; }
8 |
9 | .limegreen, .limegreen-important { color: limegreen !important; }
10 |
11 | .cyan, .cyan-important { color: cyan !important; }
12 |
13 | .darkcyan, .darkcyan-important { color: darkcyan !important; }
14 |
15 | :root { --md-text-font: Roboto; --md-code-font: Roboto Mono; }
16 |
17 | .md-typeset { font-feature-settings: "kern" 0; }
18 |
19 | .md-typeset #references, .md-typeset .no-underline { margin-bottom: 0; padding-bottom: 0; border: none; }
20 |
21 | .md-typeset h2 { padding-bottom: 0.2em; border-bottom: 1px solid #d3d3d3; }
22 |
23 | .md-typeset h3 { padding-bottom: 0.2em; border-bottom: 1px dashed #d3d3d3; }
24 |
25 | .md-typeset h4 { font-size: 1.17em; }
26 |
27 | .md-typeset h5 { font-size: 1.08em; }
28 |
29 | .md-typeset h6 { font-size: 1em; }
30 |
31 | .md-typeset dt { font-weight: bold; }
32 |
33 | .md-typeset img { display: block; margin-left: auto; margin-right: auto; }
34 |
35 | .md-typeset .img-inline { display: inline-block; vertical-align: text-bottom; }
36 |
37 | .md-typeset .img-border { border: 1px solid black; }
38 |
39 | .md-typeset kbd { box-shadow: 0 0 0 0.05rem var(--md-default-fg-color--lighter), 0 0.1rem 0 var(--md-default-fg-color--lighter), inset 0 -0.1rem 0.2rem var(--md-default-bg-color); }
40 |
41 | .md-typeset .admonition, .md-typeset details { font-size: 0.9em; }
42 |
43 | .md-typeset .md-typeset__table { display: block; margin-left: auto; margin-right: auto; }
44 |
45 | .md-typeset .md-typeset__table > table { display: table; width: auto; margin: 0 auto; }
46 |
47 | .md-typeset p.caption { text-align: center; font-size: 0.94em; color: grey; margin: -0.7em 0 0; }
48 |
49 | .md-typeset .footnote hr { margin-top: 0.2em; }
50 |
--------------------------------------------------------------------------------
/docs/css/extra.scss:
--------------------------------------------------------------------------------
1 | // Manually compile this file:
2 | //
3 | // scss extra.scss > extra.css
4 |
5 | $color-codes: red, darkred, orangered, green, limegreen, cyan, darkcyan;
6 | @each $color in $color-codes {
7 | .#{"" + $color}, .#{"" + $color}-important {
8 | color: #{$color} !important;
9 | }
10 | }
11 |
12 | :root {
13 | --md-text-font: Roboto;
14 | --md-code-font: Roboto Mono;
15 | }
16 |
17 | .md-typeset {
18 | font-feature-settings: "kern" 0;
19 |
20 | #references,
21 | .no-underline {
22 | margin-bottom: 0;
23 | padding-bottom: 0;
24 | border: none;
25 | }
26 |
27 | h2 {
28 | padding-bottom: 0.2em;
29 | border-bottom: 1px solid #d3d3d3;
30 | }
31 |
32 | h3 {
33 | padding-bottom: 0.2em;
34 | border-bottom: 1px dashed #d3d3d3;
35 | }
36 |
37 | h4 {
38 | font-size: 1.17em;
39 | }
40 |
41 | h5 {
42 | font-size: 1.08em;
43 | }
44 |
45 | h6 {
46 | font-size: 1em;
47 | }
48 |
49 | dt {
50 | font-weight: bold;
51 | }
52 |
53 | img {
54 | display: block;
55 | margin-left: auto;
56 | margin-right: auto;
57 | }
58 |
59 | .img-inline {
60 | display: inline-block;
61 | vertical-align: text-bottom;
62 | }
63 | .img-border {
64 | border: 1px solid black;
65 | }
66 |
67 | kbd {
68 | box-shadow: 0 0 0 0.05rem var(--md-default-fg-color--lighter), 0 0.1rem 0 var(--md-default-fg-color--lighter),
69 | inset 0 -0.1rem 0.2rem var(--md-default-bg-color);
70 | }
71 |
72 | .admonition,
73 | details {
74 | font-size: 0.9em;
75 | }
76 |
77 | .md-typeset__table {
78 | display: block;
79 | margin-left: auto;
80 | margin-right: auto;
81 |
82 | > table {
83 | display: table;
84 | width: auto;
85 | margin: 0 auto;
86 | }
87 | }
88 |
89 | p.caption {
90 | text-align: center;
91 | font-size: 0.94em;
92 | color: grey;
93 | margin: -0.7em 0 0;
94 | }
95 |
96 | .footnote hr {
97 | margin-top: 0.2em;
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/docs/css/roboto.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'Roboto';
3 | font-style: italic;
4 | font-weight: 400;
5 | font-display: fallback;
6 | src: url(https://fonts.gstatic.com/s/roboto/v29/KFOkCnqEu92Fr1Mu51xIIzc.ttf) format('truetype');
7 | }
8 | @font-face {
9 | font-family: 'Roboto';
10 | font-style: normal;
11 | font-weight: 300;
12 | font-display: fallback;
13 | src: url(https://fonts.gstatic.com/s/roboto/v29/KFOlCnqEu92Fr1MmSU5fBBc9.ttf) format('truetype');
14 | }
15 | @font-face {
16 | font-family: 'Roboto';
17 | font-style: normal;
18 | font-weight: 400;
19 | font-display: fallback;
20 | src: url(https://fonts.gstatic.com/s/roboto/v29/KFOmCnqEu92Fr1Mu4mxP.ttf) format('truetype');
21 | }
22 | @font-face {
23 | font-family: 'Roboto';
24 | font-style: normal;
25 | font-weight: 700;
26 | font-display: fallback;
27 | src: url(https://fonts.gstatic.com/s/roboto/v29/KFOlCnqEu92Fr1MmWUlfBBc9.ttf) format('truetype');
28 | }
29 | @font-face {
30 | font-family: 'Roboto Mono';
31 | font-style: normal;
32 | font-weight: 400;
33 | font-display: fallback;
34 | src: url(https://fonts.gstatic.com/s/robotomono/v13/L0xuDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vq_ROW9.ttf) format('truetype');
35 | }
36 |
--------------------------------------------------------------------------------
/docs/images/heading-id-failure.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/images/heading-id-failure.png
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | icon: material/hand-wave-outline
3 | ---
4 |
5 | # 欢迎
6 |
7 | !!! success "本文已完稿并通过审阅,是正式版本。"
8 |
9 | 各位读者们,欢迎阅读《Linux 101》在线讲义(以下统称“本书”)。本书由中国科学技术大学 Linux 用户协会的数名优秀成员协力编写,用于配套和延展“Linux 101”校内社团活动。其面向 Linux 零基础读者,从计算机操作系统和 Linux 的起源讲起,深入浅出带领读者一步步逐渐掌握最必要的 Linux 实用知识,并在其中感悟到社区开源文化的魅力。本书旨在成为一份为有志探索和钻研 Linux 这片大陆的开拓者们准备的路引,若潜心研读则必有所获,现在就开始你的旅程吧。
10 |
11 | **在开始之前,强烈建议先仔细研读一遍阅读指南。**
12 |
13 | ## 阅读指南 {#guidelines}
14 |
15 | ### 范围与目的 {#coverage-and-aim}
16 |
17 | 本书是一份 Linux 的基础教程,目标是引导不了解 Linux 的读者掌握基础且实用的知识并领略社区开源文化的魅力。若仔细研读完本书,你就能:
18 |
19 | - 了解 Linux 历史和开源社区文化
20 | - 安装 Linux 发行版并利用内置实用手册
21 | - 在体验中亲自感受 Linux 的专业能力
22 | - 自如地操作与配置你的 Linux 系统
23 | - 理解 Linux 操作系统的思想
24 | - 使用 Linux 生态高效编程开发
25 | - 以及灵活运用其它 Linux 生态中十分流行的各类工具等等
26 |
27 | 对于偏向实用需求的计算机开发或学术研究的读者,通过阅读本书可以快速掌握高效且流行的业界开发和学界研究的工具;对于基于兴趣和探索新事物的目的而前来阅读的读者,本书则能为他们提供一个更广阔的 Linux 世界的面貌和一份快速入门的助力。
28 |
29 | ### 结构安排 {#structure}
30 |
31 | 本书包括前言、正文及附录。正文共包括 9 章,每章的内容如下:
32 |
33 | - 第 1 章主要讲述了 Linux 的文化和生态,并提供了安装流程。
34 | - 第 2 章提供了一个自定义 Linux 系统和利用其设立自己专属服务器的体验指南,这一部分应配合课堂分发的工具包和虚拟机使用。
35 | - 第 3 章简单提供了基本的软件安装和文件操作知识。
36 | - 第 4 章全面介绍了系统的进程、服务和任务,是一块丰富的核心内容,读者应反复阅读。
37 | - 第 5 章讲解了用户和用户组、文件权限以及文件系统层次结构等系统管理员必知的文件管理核心知识。
38 | - 第 6 章提供了网络、文本处理和脚本编程的知识,让读者能利用脚本的力量完成综合任务。
39 | - 第 7 章简单展示了十分流行的在 Linux 环境下进行 C++ / Python 开发的方法。
40 | - 第 8 章介绍了近年来业界十分热门的容器隔离技术和 Docker 容器管理软件。
41 | - 第 9 章为利用脚本编程进行了进阶的说明,包括十分关键的正则表达式。
42 |
43 | 附录包括用语表和若干追加主题。其中,用语表包含了阅读全书中遇到的专业术语和用语的详细解释;每篇追加主题则包括诸多与本书极为相关的额外知识并已经进行了系统化的组织,提升读者的求知体验。
44 |
45 | ### 自主阅读与拓展阅读 {#self-reading}
46 |
47 | 在每一章的正文中,标有 \* 的段落都是自主阅读的部分。自主阅读的部分不会在配套的 Linux 101 活动的授课中提及,但通常也是有趣和重要的内容,推荐读者自行阅读。
48 |
49 | 每一章除了正文之外,还会有一篇拓展阅读专页,其与正文平级。每一章的拓展阅读专页记录了若干个与本章主题息息相关的额外知识,它通常都是正文中所提到的知识点的拓展和延申,供感兴趣和有需要的读者参阅。这些拓展阅读的编写并不完全依据本书大纲的知识结构的顺序,因此可能需要后续章节甚至是外部知识作为前置才能阅读。如果遇到这种情况,本书会在对应拓展阅读节的开头标注出所需的额外前置知识。
50 |
51 | ### 思考题 {#thinking-question}
52 |
53 | 在每一章的正文的最后,通常都会有若干道思考题。其中的一部分题目用于对本章所学内容的回顾和巩固,另一部分题目则鼓励读者通过利用互联网渠道自主寻找答案。建议读者在阅读完一章的正文后立刻尝试回答这些思考题,这么做可以有助于读者快速提升计算机的技术素养。
54 |
55 | ### 其它资料 {#extras}
56 |
57 | - [记号约定](notations.md)中约定了本书中常用的符号与注记形式。在研读本书的过程中可随时参考。
58 | - [功劳簿](credits.md)列出了所有为编写本书做出贡献的朋友们。
59 | - Linux 101 活动于 2020 年的直播记录视频可以在 [LUG FTP](https://ftp.lug.ustc.edu.cn/101/videos) 找到,也可以在 [YouTube](https://www.youtube.com/playlist?list=PLkqsPhn1XtD2h_o5-lY3exDRXtKBiKwkk) 上在线观看。
60 | - 对于服务器运维等较为深入的应用场景,我们另外启动了《[Linux 201](https://201.ustclug.org/)》项目,作为本书的进阶版,可供感兴趣的读者参考。
61 |
62 | ## 联系我们 {#contact-us}
63 |
64 | 本书的成长离不开大家的帮助。本书的源代码存放在 [:fontawesome-brands-github: Linux101-docs 仓库](https://github.com/ustclug/Linux101-docs) 中,所有内容以 [:fontawesome-brands-creative-commons: CC BY-SA 4.0 协议](https://creativecommons.org/licenses/by-sa/4.0/)开放。
65 |
66 | 若您对本书有任何意见或建议,可以在本书的 GitHub 仓库中提出 Issue,也欢迎致信 {: .img-inline },我们重视您的想法。
67 |
--------------------------------------------------------------------------------
/docs/notations.md:
--------------------------------------------------------------------------------
1 | # 记号约定
2 |
3 | ## 命令行环境 {#command-line}
4 |
5 | 对于在终端(命令行)环境下执行的连续几条命令及其输出,用以下方式表示:
6 |
7 | ```console
8 | $ 普通权限执行的命令 1 # 注释 1
9 | 命令 1 的输出
10 |
11 | # 高级权限执行的命令 2 # 注释 2
12 | 命令 2 的输出
13 |
14 | ```
15 |
16 | 命令开头的 `$` 代表此命令使用普通用户权限运行;命令开头的 `#` 代表执行命令需要使用 root 权限(最高权限),一般等价于 `$ sudo 该命令`。命令行末尾的 `#` 代表此符号与其后的内容都是注释,不参与执行。
17 |
18 | 命令行中的参数、占位符通常使用尖括号包裹,如:
19 |
20 | ```console
21 | $ sudo apt install <包名1>
22 | $ sudo apt remove <包名2>
23 | ```
24 |
25 | ## 用户 {#users}
26 |
27 | 在没有特殊说明的情况下,本书假设你的用户名为 `ustc`,这也是我们提供的虚拟机镜像中的默认用户名。
28 |
--------------------------------------------------------------------------------
/docs/postface.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/postface.md
--------------------------------------------------------------------------------
/docs/preface.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ustclug/Linux101-docs/6b22f223c1a38d7b7146bdaf6a1ae37ebeeb50a6/docs/preface.md
--------------------------------------------------------------------------------
/mkdocs.yml:
--------------------------------------------------------------------------------
1 | site_name: Linux 101
2 | site_description: Linux 101 课程讲义
3 | site_author: LUG@USTC
4 | site_url: https://101.lug.ustc.edu.cn/
5 | repo_name: ustclug/Linux101-docs
6 | repo_url: https://github.com/ustclug/Linux101-docs
7 | copyright: Brought to you by LUG@USTC. Available freely under the CC BY-SA 4.0 license.
8 |
9 | theme:
10 | name: 'material'
11 | language: 'zh'
12 | font: false
13 | palette:
14 | - scheme: default
15 | media: "(prefers-color-scheme: light)"
16 | primary: indigo
17 | accent: indigo
18 | toggle:
19 | icon: material/weather-night
20 | name: 切换至深色模式
21 | - scheme: slate
22 | media: "(prefers-color-scheme: dark)"
23 | primary: blue
24 | accent: blue
25 | toggle:
26 | icon: material/weather-sunny
27 | name: 切换至浅色模式
28 | icon:
29 | logo: fontawesome/brands/linux
30 | repo: octicons/mark-github-16
31 | features:
32 | - content.action.edit
33 | - content.action.view
34 | - content.code.copy
35 | - content.tooltips
36 | - navigation.footer
37 | - navigation.indexes
38 | - navigation.top
39 | - navigation.tracking
40 |
41 | markdown_extensions:
42 | - admonition
43 | - attr_list
44 | - footnotes
45 | - pymdownx.details
46 | - pymdownx.emoji:
47 | emoji_index: !!python/name:material.extensions.emoji.twemoji
48 | emoji_generator: !!python/name:material.extensions.emoji.to_svg
49 | - pymdownx.superfences:
50 | custom_fences:
51 | - name: mermaid
52 | class: mermaid
53 | format: !!python/name:pymdownx.superfences.fence_code_format
54 | - pymdownx.inlinehilite
55 | - pymdownx.keys
56 | - pymdownx.superfences
57 | - markdown.extensions.def_list
58 | - toc:
59 | permalink: true
60 |
61 | extra:
62 | social:
63 | - icon: octicons/globe-16
64 | link: https://lug.ustc.edu.cn/
65 | - icon: octicons/mark-github-16
66 | link: https://github.com/ustclug
67 | analytics:
68 | provider: google
69 | property: G-Q8WSZQS8E1
70 |
71 | extra_css:
72 | - 'css/extra.css'
73 | - 'https://101.lug.ustc.edu.cn/_extra/roboto.css'
74 | - 'https://101.lug.ustc.edu.cn/_extra/roboto-mono/stylesheet.css'
75 |
76 | nav:
77 | - 负一:计划规格:
78 | - 章节编写指导: Spec/writing.md
79 | - 演示文稿规格: Spec/slide.md
80 | - 零:欢迎:
81 | - index.md
82 | - 记号约定: notations.md
83 | - 功劳簿: credits.md
84 | - 一:初识 Linux:
85 | - Ch01/index.md
86 | - 拓展阅读: Ch01/supplement.md
87 | - 思考题解答: Ch01/solution.md
88 | - 二:个性化配置与建站体验:
89 | - Ch02/index.md
90 | - 拓展阅读: Ch02/supplement.md
91 | - 思考题解答: Ch02/solution.md
92 | - 三:软件安装与文件操作:
93 | - Ch03/index.md
94 | - 拓展阅读: Ch03/supplement.md
95 | - 思考题解答: Ch03/solution.md
96 | - 四:进程、前后台、服务与例行性任务:
97 | - Ch04/index.md
98 | - 拓展阅读: Ch04/supplement.md
99 | - 思考题解答: Ch04/solution.md
100 | - 五:用户与用户组、文件权限、文件系统层次结构:
101 | - Ch05/index.md
102 | - 拓展阅读: Ch05/supplement.md
103 | - 思考题解答: Ch05/solution.md
104 | - 六:网络、文本处理工具与 Shell 脚本:
105 | - Ch06/index.md
106 | - 拓展阅读: Ch06/supplement.md
107 | - 思考题解答: Ch06/solution.md
108 | - 七:Linux 上的编程:
109 | - Ch07/index.md
110 | - 拓展阅读: Ch07/supplement.md
111 | - 八:Docker:
112 | - Ch08/index.md
113 | - 拓展阅读: Ch08/supplement.md
114 | - 九:Shell 高级文本处理与正则表达式:
115 | - Ch09/index.md
116 | - 拓展阅读: Ch09/supplement.md
117 | - 附录:
118 | - Markdown 教程: Appendix/markdown.md
119 | - man 文档的一些示例: Appendix/man.md
120 | - 其他的 Linux 发行版:技术差异简介: Appendix/distribution.md
121 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "linux101-docs",
3 | "version": "0.0.1",
4 | "lockfileVersion": 2,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "linux101-docs",
9 | "version": "0.0.1",
10 | "license": "CC BY-SA 4.0",
11 | "devDependencies": {
12 | "prettier": "^2.8.8"
13 | }
14 | },
15 | "node_modules/prettier": {
16 | "version": "2.8.8",
17 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz",
18 | "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
19 | "dev": true,
20 | "bin": {
21 | "prettier": "bin-prettier.js"
22 | },
23 | "engines": {
24 | "node": ">=10.13.0"
25 | },
26 | "funding": {
27 | "url": "https://github.com/prettier/prettier?sponsor=1"
28 | }
29 | }
30 | },
31 | "dependencies": {
32 | "prettier": {
33 | "version": "2.8.8",
34 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz",
35 | "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
36 | "dev": true
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "linux101-docs",
3 | "version": "0.0.1",
4 | "description": "Prettier package.json for ustclug Linux 101 website",
5 | "scripts": {
6 | "check": "prettier . --check",
7 | "fix": "prettier . --write"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git+https://github.com/ustclug/Linux101-docs.git"
12 | },
13 | "license": "CC BY-SA 4.0",
14 | "bugs": {
15 | "url": "https://github.com/ustclug/Linux101-docs/issues"
16 | },
17 | "homepage": "https://101.lug.ustc.edu.cn",
18 | "devDependencies": {
19 | "prettier": "^2.8.8"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | mkdocs>=1.5.3
2 | mkdocs-material>=9.4.6
3 | jieba
4 |
--------------------------------------------------------------------------------