Skip to content

Commit a97859b

Browse files
committed
Add report template and docs
1 parent 0e0255d commit a97859b

File tree

6 files changed

+348
-0
lines changed

6 files changed

+348
-0
lines changed

README.md

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
# ShellLab:构建 Unix Shell 🐚
2+
3+
## 实验说明文档
4+
5+
### 1. 实验目标 🎯
6+
7+
通过编写一个支持作业控制的Unix Shell程序,深入理解进程控制和信号处理的核心概念。
8+
9+
### 2. 实验概述 📝
10+
11+
Shell是一个交互式命令行解释器,负责接收用户指令并执行相应程序。本实验要求实现一个简化版的Shell,包含基本的命令解析、进程创建、信号处理、作业控制和I/O重定向功能。实验提供了骨架代码和完整的命令解析器,你需要实现核心执行逻辑和控制机制。
12+
13+
关于Shell原理和实现方法的详细指导,请参阅[实验指导文档](docs/guide.md)
14+
15+
### 3. 实验要求 📋
16+
17+
#### 3.1 基本功能(必做)
18+
19+
- 命令提示符为`"tsh> "`
20+
- 执行命令行程序(前台或后台运行)
21+
- 实现作业控制(bg、fg等)
22+
- 处理键盘信号(Ctrl+C、Ctrl+Z)
23+
- 实现基本的I/O重定向(`<``>`
24+
- 回收终止的子进程
25+
26+
#### 3.2 内建命令
27+
28+
- `quit`/`exit`:终止shell
29+
- `jobs`:列出所有后台作业
30+
- `bg <job>`:将一个停止状态的后台作业转为运行状态
31+
- `fg <job>`:将一个后台作业转为前台运行
32+
- `cd <dir>`:切换当前工作目录
33+
34+
#### 3.3 附加任务(选做)
35+
36+
- 基本管道支持(`|`
37+
- 复杂管道链支持(如`cmd1 | cmd2 | cmd3`
38+
- 环境变量展开功能(`$VAR``${VAR}`
39+
- 命令替换功能(`$(command)`
40+
- 终端控制机制(支持vim、gdb等交互式程序)
41+
- 脚本执行功能
42+
- 支持PATH环境变量
43+
44+
### 4. 文件结构与接口 🗂️
45+
46+
项目采用模块化设计,文件结构如下:
47+
48+
```
49+
.
50+
├── include
51+
│ └── shell.h # 头文件,包含函数声明与数据结构
52+
├── Makefile # 用于编译项目
53+
└── src
54+
├── builtins.c # 内置命令的实现
55+
├── jobs.c # 作业控制相关函数
56+
├── main.c # 主函数
57+
├── parser.c # 命令解析相关函数
58+
├── shell.c # Shell 核心逻辑
59+
├── signals.c # 信号处理函数
60+
└── utils.c # 工具函数
61+
```
62+
63+
命令解析器提供了以下结构:
64+
65+
```c
66+
typedef struct command {
67+
char *argv[MAXARGS]; /* 命令和参数 */
68+
int argc; /* 参数数量 */
69+
char *infile; /* 输入重定向文件 */
70+
char *outfile; /* 输出重定向文件 */
71+
int append; /* 输出重定向的追加模式标志 */
72+
struct command *next; /* 管道:指向下一个命令 */
73+
} command_t;
74+
75+
int parse_command_line(const char *cmdline, command_t **cmd, int *bg);
76+
```
77+
78+
需要实现的主要函数:
79+
80+
- `builtins.c`: `builtin_cmd`, `do_bgfg`
81+
- `shell.c`: `eval`, `eval_script`(选做)
82+
- `signals.c`: `sigchld_handler`, `sigint_handler`, `sigtstp_handler`
83+
- `parser.c`: `env_expand`, `command_substitute`(选做)
84+
85+
### 5. 测试方法 🧪
86+
87+
实验提供了测试脚本,可以用于验证你的 Shell 实现是否正确:
88+
89+
```bash
90+
python grader.py
91+
```
92+
93+
或者使用如下的方式运行单个测试点:
94+
95+
```bash
96+
python grader.py <test_id>
97+
```
98+
99+
### 6. 评分标准 💯
100+
101+
- 基础功能实现(60%)
102+
- 命令执行与进程管理
103+
- 信号处理
104+
- 作业控制
105+
- I/O重定向
106+
- 代码风格与注释
107+
108+
- 选做任务(20%)
109+
- 任选两项选做任务完成
110+
- 或使用 Rust 实现
111+
112+
- 实验报告与代码质量(20%)
113+
- 实现思路清晰
114+
- 关键功能分析
115+
- 测试结果
116+
- 代码结构合理
117+
- 注释完善
118+
- 错误处理充分
119+
120+
### 7. 提交方式 📤
121+
122+
使用 GitHub Classroom 进行提交。请你确保所有代码已提交到你的对应仓库,GitHub Actions 会自动运行测试,其输出作为我们的评分依据。
123+
124+
提交截止日期:2025年3月21日 23:59
125+
126+
提交内容:
127+
1. 所有源代码文件(通过 GitHub 仓库提交)
128+
2. 实验报告(PDF格式,上传至仓库根目录)
129+
130+
请确保在截止日期前完成最终提交。GitHub 会记录你的所有提交历史,我们将以截止日期前的最后一次提交作为最终版本进行评分。

docs/guide.md

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
2+
3+
# ShellLab:构建 Unix Shell 🐚
4+
5+
## 实验指导文档
6+
7+
### Unix Shell 的工作原理 🔍
8+
9+
Unix Shell 是操作系统与用户交互的接口,它接收用户输入的命令,解析后执行相应的程序。Shell 的核心工作流程可以概括为:读取命令、解析命令、执行命令、等待执行完成、循环重复以上步骤。在这个过程中涉及进程控制、信号处理、I/O重定向等多个系统概念。
10+
11+
当用户输入命令后,Shell 会首先检查是否为内建命令。内建命令由 Shell 进程自身执行,不需要创建子进程。对于非内建命令,Shell 会创建子进程并在其中执行相应程序。这种设计使得 Shell 能够提供统一的命令执行环境,同时保持自身的稳定运行。
12+
13+
### 进程控制与作业管理 🧵
14+
15+
进程是程序的执行实例,Shell 通过 `fork()` 系统调用创建子进程,然后通过 `execve()` 系统调用在子进程中加载并执行新程序。这种"fork-exec"模式是 Unix 系统进程创建的基础机制。
16+
17+
作业控制允许用户管理多个并行任务。在 Shell 中,一个作业可以在前台或后台运行。前台作业会占用终端,Shell 会等待其完成;后台作业则在后台执行,Shell 可以同时接受新的命令。用户可以通过 `bg``fg` 命令在前台和后台间切换作业,通过 `jobs` 命令查看所有后台作业。
18+
19+
实现作业控制需要跟踪所有子进程的状态。在本实验中,我们使用全局作业表记录作业信息,包括进程ID、作业ID、状态和命令行。每个作业都有唯一的作业ID,可以通过 `%JID` 或 PID 引用。
20+
21+
### 信号处理机制 📡
22+
23+
信号是一种软件中断,用于通知进程发生了某种事件。Shell 需要处理三类关键信号:
24+
25+
SIGCHLD 信号在子进程状态改变时发送给父进程。Shell 需要捕获这个信号并回收终止的子进程,防止它们变成僵尸进程。此外,还需要更新作业表以反映子进程的最新状态。
26+
27+
SIGINT 信号在用户按下 Ctrl+C 时产生,通常用于中断前台程序。Shell 应该捕获此信号并将其转发给前台进程组,而不是自己终止。
28+
29+
SIGTSTP 信号在用户按下 Ctrl+Z 时产生,用于暂停前台程序。Shell 同样需要捕获并转发此信号给前台进程组。
30+
31+
信号处理需要特别注意竞态条件。例如,在 `fork()` 子进程后,父进程可能在将新作业添加到作业表之前就收到 SIGCHLD 信号,导致子进程状态无法正确跟踪。解决方案是在 `fork()` 前使用 `sigprocmask()` 暂时阻塞 SIGCHLD 信号,在完成作业表更新后再解除阻塞。
32+
33+
### I/O 重定向与管道 🔄
34+
35+
I/O 重定向允许程序从文件而非标准输入读取数据,或将输出写入文件而非标准输出。Shell 使用 `<` 符号表示输入重定向,`>``>>` 符号表示输出重定向(后者为追加模式)。
36+
37+
实现重定向的关键是使用 `open()` 系统调用打开相应文件,然后通过 `dup2()` 系统调用将标准输入/输出文件描述符重定向到打开的文件。对于子进程,这些重定向操作应在 `execve()` 之前完成。
38+
39+
管道是一种进程间通信机制,允许一个进程的输出直接作为另一个进程的输入。Shell 使用 `|` 符号表示管道连接。实现管道需要使用 `pipe()` 系统调用创建管道,然后分别 `fork()` 出管道两端的进程,并通过 `dup2()` 连接管道与标准输入/输出。
40+
41+
对于复杂的管道链,可以采用递归方式处理,每次处理链中的一个命令,并正确设置其输入输出重定向。
42+
43+
### 终端控制机制 🖥️
44+
45+
终端控制是 Shell 高级功能之一,它涉及进程组、会话和控制终端的概念。每个终端同一时刻只能被一个进程组控制,这个进程组称为前台进程组。其他进程组为后台进程组,它们无法直接从终端读取输入。
46+
47+
正确实现终端控制需要使用 `tcsetpgrp()` 函数设置终端的前台进程组。当作业在前台和后台之间切换时,Shell 需要相应地更新控制终端的所有权。此外,还需要正确处理终端属性和模式切换,以支持 vim、gdb 等交互式程序的运行。
48+
49+
实现终端控制的关键步骤:
50+
1.`fork()` 后,子进程应调用 `setpgid(0, 0)` 创建新的进程组
51+
2. 对于前台作业,Shell 应调用 `tcsetpgrp()` 将终端控制权交给该作业的进程组
52+
3. 作业完成或被挂起后,Shell 应重新获取终端控制权
53+
4. 将后台作业切换到前台时,应使用 `tcsetpgrp()` 更新终端控制权
54+
55+
### 环境变量与命令替换 🧩
56+
57+
环境变量是操作系统维护的一组动态值,可以被程序访问。Shell 支持环境变量展开功能,允许用户在命令中使用 `$VAR``${VAR}` 语法引用环境变量的值。
58+
59+
实现环境变量展开需要识别命令行中的变量引用,使用 `getenv()` 函数获取对应的环境变量值,然后替换原字符串中的变量引用。这通常需要使用正则表达式或字符串匹配算法。
60+
61+
命令替换是 Shell 的高级功能,允许用户使用 `$(command)` 语法将命令的输出作为另一个命令的参数。实现命令替换需要创建子进程执行被替换的命令,捕获其输出,然后将输出替换到原命令行中。这通常需要使用管道和临时文件。
62+
63+
### 实现策略与常见问题 🛠️
64+
65+
实现 Shell 是一个复杂任务,建议采用渐进式方法:先实现基本命令执行,再添加信号处理,然后是作业控制,最后是I/O重定向和管道。每完成一个阶段就进行测试,确保功能正确再进行下一步。
66+
67+
常见问题及解决方案:
68+
69+
竞态条件:使用 `sigprocmask()` 在关键区域暂时阻塞信号,避免信号处理函数与主程序逻辑的竞争。
70+
71+
僵尸进程:在 SIGCHLD 处理函数中使用 `waitpid()` 及时回收终止的子进程,更新作业表状态。
72+
73+
信号转发:使用负的PID值(如 `kill(-pid, sig)`)向整个进程组发送信号,确保前台作业的所有进程都能收到信号。
74+
75+
前台等待:实现 `waitfg()` 函数等待前台作业完成,可以使用轮询方式,定期检查作业状态。
76+
77+
资源泄漏:确保关闭所有打开的文件描述符,释放分配的内存,避免资源泄漏。
78+
79+
错误处理:检查每个系统调用的返回值,及时处理错误情况,提高程序鲁棒性。
80+
81+
### 深入理解Shell与操作系统的交互 🔄
82+
83+
Shell 是理解操作系统核心概念的绝佳窗口。通过实现 Shell,你将深入了解进程创建与终止、信号处理、I/O操作、进程间通信等机制。这些概念不仅是操作系统的基础,也是系统编程的核心知识。
84+
85+
在实现过程中,建议查阅相关系统调用的手册页(man pages),理解它们的参数和返回值,尤其是错误情况的处理。同时,学会使用调试工具如 gdb 和 strace,它们可以帮助你跟踪程序执行和系统调用。
86+
87+
希望通过本实验,你能对操作系统的进程管理、信号处理和I/O机制有更深入的理解,并培养系统级编程的能力。祝你实验顺利!

report/README.md

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# 报告模板通用指南
2+
3+
通常我们提供包括但不限于 Markdown 格式的报告模板,以便于大家准确精炼地展示助教想要了解的信息。
4+
5+
如果你不使用我们提供的模板,你也需要参考我们提供的模板内容来组织你的报告。
6+
7+
无论我们提供何种格式的报告模板,或你自己选择的制作格式,你都需要在提交时同时提交**源文件****转换出的 PDF** ,以便于助教查阅。
8+
9+
> 比如 .tex 和 .pdf 文件。
10+
11+
## 通用报告要求
12+
13+
我们有一些跨 lab 共通的报告要求:
14+
15+
1. 不要贴大段的代码,你可以通过以下手段来避免:
16+
- 使用伪代码
17+
- 用注释替代一些不重要的部分
18+
- 如果确实必要,请一小段一小段地复制,并配合文字解释
19+
2. 如果某段内容很长,请用“总分”的格式来组织,即先分点简述一下,再开始展开。同时,最好先讲你觉得最重要的部分。
20+
3. 不要写大段“助教知道”的内容,除非助教要求,比如解释非你修改/写的代码的内容。
21+
4. 尽量精简,不要用 AI 生成工具的车轱辘话来应付报告,我们宁愿你写得很短。
22+
23+
## Markdown 转 PDF
24+
25+
### 方法一:Typora
26+
27+
https://typora.io/#download
28+
29+
> 我们可能会于上机课上解释 Typora 的用法。但互联网上的资料已经足够丰富。
30+
31+
### 方法二:pandoc
32+
33+
> 这个环境配置占用的空间比较大(>2G),而且转化效果和前者相比,相去甚远,除非迫不得已,推荐使用方法一。
34+
35+
Ubuntu 系统下安装(我们提供的服务器会提前安装好,WSL2 用户可以参考):
36+
37+
```bash
38+
sudo apt-get install pandoc texlive-xetex texlive-lang-chinese
39+
```
40+
41+
检查系统安装的中文字体:
42+
43+
```bash
44+
fc-list :lang=zh
45+
```
46+
47+
它的输出形如:
48+
49+
```bash
50+
/usr/share/fonts/truetype/arphic/uming.ttc: AR PL UMing TW MBE:style=Light
51+
/usr/share/fonts/truetype/arphic-bkai00mp/bkai00mp.ttf: AR PL KaitiM Big5,文鼎PL中楷:style=Regular
52+
/usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf: Droid Sans Fallback:style=Regular
53+
/usr/share/fonts/truetype/arphic-gkai00mp/gkai00mp.ttf: AR PL KaitiM GB,文鼎PL简中楷:style=Regular
54+
/usr/share/fonts/truetype/arphic-gbsn00lp/gbsn00lp.ttf: AR PL SungtiL GB,文鼎PL简报宋:style=Regular
55+
/usr/share/fonts/truetype/arphic/uming.ttc: AR PL UMing TW:style=Light
56+
```
57+
58+
分别对应字体:
59+
60+
- "AR PL UMing TW MBE"
61+
- "AR PL KaitiM Big5" 或 "文鼎PL中楷"
62+
- "Droid Sans Fallback"
63+
64+
等,即文件名后面到冒号前的部分
65+
66+
转化命令:
67+
68+
```bash
69+
pandoc -s report.md -o report.pdf --pdf-engine=xelatex -V CJKmainfont="Droid Sans Fallback"
70+
```
71+
72+
其中 "Droid Sans Fallback" 可以换成任何你系统中存在的中文字体,但如果报 WANING 支持不全,则需要尝试其他字体。

report/imgs/autograder.png

126 KB
Loading

report/imgs/example.jpg

48.8 KB
Loading

report/template.md

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# ShellLab 报告
2+
3+
姓名:栗志飞
4+
学号:2024011801
5+
6+
## Part A: 思路简述
7+
8+
<!-- 200字以内简述你的Shell实现思路,重点说明:
9+
1. 核心方法的流程,依次做了什么
10+
2. 关键数据结构的设计,如何组织和管理作业控制
11+
-->
12+
13+
## Part B: 具体实现分析
14+
15+
### 命令解析与执行
16+
<!-- 300字以内,描述:
17+
1. 如何处理不同类型的命令(内建命令/外部命令)
18+
2. 如何实现作业控制(前台/后台)
19+
3. 实现中的关键优化
20+
4. 关键的错误处理,一些边界情况与 sanity check
21+
-->
22+
23+
### 信号处理
24+
<!-- 300字以内,描述:
25+
1. 支持的信号类型
26+
2. 信号处理方法
27+
3. 关键的错误处理
28+
-->
29+
30+
### I/O重定向与管道
31+
<!-- 300字以内,描述:
32+
1. 如何实现输入输出重定向
33+
2. 管道的实现方法
34+
3. 对齐等特殊处理
35+
-->
36+
37+
## Part C: 关键难点解决
38+
<!-- 选择2-3个最有技术含量的难点:
39+
1. 具体难点描述
40+
2. 你的解决方案
41+
3. 方案的效果
42+
示例难点:
43+
- 作业控制实现
44+
- 信号处理的边界情况
45+
- 管道与重定向的结合
46+
-->
47+
48+
## Part D: 实验反馈
49+
<!-- 你的反馈对我们至关重要
50+
可以从实验设计,实验文档,框架代码三个方面进行反馈,具体衡量:
51+
1. 实验设计:实验难度是否合适,实验工作量是否合理,是否让你更加理解Shell,Shell够不够有趣
52+
2. 实验文档:文档是否清晰,哪些地方需要补充说明
53+
3. 框架代码:框架代码是否易于理解,接口设计是否合理,实验中遇到的框架代码的问题(请引用在 repo 中你提出的 issue)
54+
-->
55+
56+
## 参考资料 (可不填)
57+
<!-- 对实现有实质帮助的资料 -->
58+
1. [资料名] - [链接] - [具体帮助点]
59+
2. ...

0 commit comments

Comments
 (0)