Vince Jiang

CSCI3150 / 作业与题解 · 本站更新

CSCI3150 HW01 · 2026T1 · Shell 分级提示

课程:CSCI3150 Introduction to Operating Systems · 2026T1 本份:Assignment One,按实现单元逐步挑战 定位:自己完成管道 Shell;卡住时只打开下一层提示,不提供完整代码或可直接拼接的伪代码。

先确认这次要做什么

依据 HW01 题目(本地课件) p.1 和教师代码包 Readme。这是 HW01,不是 Lab02。截止时间为 2026-10-13 23:59。

  • 在 simple-execute.c 的 shell_execute(char **args, int argc) 中支持 0–3 个管道,即最多 4 条命令。每个管道连接相邻命令的 stdout 和 stdin。
  • 题目保证每个 | 前后至少有一个空格;ls| 不在测试范围。无管道命令与独立 EXIT 必须仍能工作。
  • 父子进程都要关闭无用管道描述符;启动全部管道命令后才等待,并在下一次提示符前等完每个孩子。
  • 可以在指定文件内添加辅助函数与头文件;不要修改 simple-shell.c 或 Makefile。只提交 simple-execute.c,注释中列实际成员姓名与 SID;最多三人一组,一名成员提交一份。
  • 题目要求使用 fork、pipe、execvp,以及 dup 或 dup2;用 wait 或 waitpid 等待。要在课程要求环境编译运行。原题这一页没有给出完整环境规格,不能仅凭本机编译就断言评分环境通过。

本页不包含已完成作答、姓名或学号。下面的练习与测试方法不是隐藏评分用例,也不是本轮已执行的测试记录。

建议怎么推进

先读懂接口、保住单命令行为,再挑战两个命令,最后扩到三、四个命令。每完成一个单元先测试,再决定看下一层提示。三级提示嵌套收起,不会一次性展开整套实现。

1 · 输入接口与计数

目标:搞清调用者传入的参数数组,以及返回值怎样影响外层循环。

提示 1 · 切入点

先读教师 main 中调用执行函数的几行,再读 get_line_args 的返回位置。不要从变量名 argc 猜约定。

提示 2 · 局部路线

在自己的草稿中记录每个槽位的地址是否为空、非空时的字符串、传入计数;不要用字符串格式打印空指针。用同一条短命令手画一份表。

提示 3 · 下一步与自检

教师解析器把末尾 NULL 槽计入返回计数。检查你的循环是否会把这个槽当成字符串;另看 main 怎样使用 shell_execute 的负返回值,不要把它与子进程退出状态混为一谈。

2 · 保住无管道与独立 EXIT

目标:改动后仍可运行原先单条命令,并按约定退出输入循环。

提示 1 · 切入点

先给“运行外部命令”和“结束这个 Shell”分配不同责任。哪一种需要查找可执行程序?

提示 2 · 局部路线

对照教师原函数,标注哪些语句只应由父进程执行、哪些只应由子进程执行;思考 exec 成功后旧代码还会不会继续。

提示 3 · 下一步与自检

用 echo EXIT 与独立 EXIT 分开验证。遇到不存在的命令时,观察是否意外出现第二个 Shell;它能帮你定位子进程失败路径是否回到了输入循环。

3 · 命令边界与参数段

目标:让每段命令只收到自己的程序名与参数,不把管道符作为普通选项传进去。

提示 1 · 切入点

目前教师解析器只做空白拆词。拿含一个独立管道符的输入,在纸上圈出属于同一个程序的词。

提示 2 · 局部路线

查看 execvp 对参数向量结尾的约定,再为每一段标注起点和终点。先设计你怎样表示这些边界,不急着创建孩子。

提示 3 · 下一步与自检

用只打印段信息的调试版本检查:每个原始非分隔词恰好归入一段,分隔符不落入任何段,最后一段也有合法结尾。你仍需要自己决定怎样在指定文件内组织这些信息。

4 · 理解管道与描述符

目标:让两个命令的数据通过管道传递,而不是都直接输出到终端。

提示 1 · 切入点

先画两个进程的标准输入和标准输出,只画数据应该流向哪里。哪些流应保持继承的连接?

提示 2 · 局部路线

把“数组下标”“文件描述符整数”“内核管道对象”分三层画。pipe 返回的端点编号不能由你假设为固定值。

提示 3 · 下一步与自检

单独挑一个需要改变的标准流,核对 dup 或 dup2 的源、目标语义;复制完成后原编号还是否占有引用?画完一个端点再继续,不先写全套首中末分支。

5 · 从两个命令扩到四个

目标:覆盖题目允许的 0–3 管道,不把双命令特例复制成许多互不一致的分支。

提示 1 · 切入点

先数相邻命令之间有多少条数据连接。单命令的退化情况也要画出来。

提示 2 · 局部路线

只选一个中间命令,分别追问它从哪里读、往哪里写。不要同时展开所有命令的实现。

提示 3 · 下一步与自检

为你自己的表示法检查首段、末段是否会访问不存在的邻居;再用同一检查办法审视只有一段的情况。孩子启动数量也应与命令段数量相符。

6 · 关闭引用与 EOF

目标:数据处理结束后消费者能结束,不因无用端点引用一直等待。

提示 1 · 切入点

先解释“现在没有数据”和“以后不会再有数据”有什么不同;消费者靠哪个条件知道输入结束?

提示 2 · 局部路线

只追踪一条管道:为父亲和每个孩子列出仍持有的读写端引用。fork 后继承的引用也要计入,不能只记计划使用的端点。

提示 3 · 下一步与自检

若消费者已读完却仍卡住,逐个排查是否还有不打算写入的进程持有写端。修改整数变量不等于关闭描述符;用你的引用表检查清理,不凭“这个进程不会写”推断 EOF。

7 · 启动与等待

目标:遵守全部启动后再等待、下次提示符前等完全部孩子的要求。

提示 1 · 切入点

给管道缓冲区想象一个有限容量。如果生产者还没等到消费者启动就把它写满,会怎样?

提示 2 · 局部路线

在纸上给父进程画时间线,标出孩子创建与等待的位置;再把一个很慢的命令放在不同阶段,找可能提前返回提示符的路径。

提示 3 · 下一步与自检

检查你记录的是实际成功创建的孩子,而不是计划数量。等待返回值和 status 的含义不同;调试时记录已经回收的 PID,确认没有少等或重复把失败当完成。

8 · 失败后的恢复与提交自查

目标:失败不污染下一条输入,交付范围符合题目。错误处理是工程自检,不是额外杜撰的评分项。

提示 1 · 切入点

挑一个系统调用失败点,停在该行,列出到此已经拥有的资源;不要一次写完所有失败分支。

提示 2 · 局部路线

选 execvp 失败做第一轮检查:此时运行失败路径的是谁?结束它后,父进程应怎样继续自己的交互职责?

提示 3 · 下一步与自检

再为你选择的另一个失败点检查“哪些端点已打开、哪些孩子确实存在”。提交前用原教师文件重新编译,确认只交指定源文件、占位署名已换为实际成员信息。本提示页不会代替你提交。

9 · 用观察证据定位卡点

目标:不靠反复加sleep猜顺序,也不靠看过完整解析才会排错。

提示 1 · 先缩小问题

找一条你自己的最短失败输入,记录症状属于没有输出、输出错误、输出正确却不结束,还是提前出现提示符。每次只改变一个输入因素,观察症状是否随之变化。

提示 2 · 分清对象

在草稿上分别写进程表与描述符表:每个PID正在执行什么阶段;每个进程的哪个编号引用哪个端点。只选卡住的那根连接追踪,不急着修改全部关闭语句。

提示 3 · 用反事实检查解释

假设你怀疑某一处多余引用,先预测它若不再存在,哪个等待条件会改变,再在自己的实现中验证。若预测与实际不符,回到引用表补漏,而不是继续随机加入close。调试输出应与命令stdout分开,以免干扰下游程序的数据。

追加自查 · 先判断反例是否真的覆盖了你的假设

如果一个短输入运行正常,问自己:它是否真的让相邻程序重叠执行,还是数据太少,恰好掩盖了等待顺序问题?如果一次输出正确,问自己:所有参与者都结束了吗,还是只有某一段输出先到了?把“数据正确”“完整结束”“能够继续接收下一条输入”分开观察。

再挑一个你自己的失败场景,画出失败前已经成功建立的资源。哪些动作尚未发生,哪些引用已经交给另一个进程?不要只按正常路径的行数倒推清理范围。这里不给固定关闭表或实现代码,目的是让你能用自己的状态图解释下一次尝试。

自己运行的测试场景

以下输入供你在自己的实现里验证。先在课程环境的普通 Shell 中观察命令本身,再在 SimpleShell 输入同样的命令并比较结果、退出与提示符时机。不要输入 $$$ 前缀。每组完成后用独立 EXIT 退出;教师读取循环未正确处理 EOF,不能把关闭输入当作此框架的正常退出方式。

先按教师 Readme 用 make 和 ./SimpleShell 启动。测试时保留自己的作答副本,不覆盖已有完成文件。

想检查什么 可输入的命令 观察什么
无管道与恢复 echo hello,再输入 echo again 每条只执行一次,下一次输入仍可用
独立控制命令 先 echo EXIT,最后独立 EXIT 普通参数与退出控制是否区分
一条管道 seq 1 10 | wc -l 与普通 Shell 比较输出与结束行为
两条管道 seq 1 10 | tail -n 4 | wc -l 消费者需要输入结束时会不会卡住
三条管道 seq 1 20 | head -n 12 | tail -n 5 | wc -l 中间阶段能否同时接收和发送
空输出 seq 1 10 | grep NO_MATCH | wc -l 没有匹配内容时仍能正常结束
较大有限输出 seq 1 200000 | wc -l 小输入偶然成功之外,是否存在启动/等待阻塞
失败后继续 no_such_command_hw01,再输入 echo alive 报错后是否多出 Shell、失去下一次输入

表中三条基础管道来自教师 Readme,其余是辅助自测,不是隐藏测试。表格中的命令可以复制;页面用表格格式转义管道符,显示值仍是普通 |。

若某条命令卡住,在另一终端检查进程关系,定位究竟是谁在读、写或等孩子;只处理这次测试启动的进程,不要用宽泛的进程名批量终止。进阶时可以在课程 Linux 环境观察系统调用和描述符,但先用自己的引用表解释现象。

想清楚后再对照

我准备查看详细解析(展开后再确认)

详细解析包含具体描述符连接、官方示例演算与完整流程,会明显降低独立挑战难度。

确认查看 HW01 Shell 详细解析

来源与证据

  • HW01 题目(本地课件) p.1:范围、截止、分组、接口与提交要求。
  • 教师原始代码包(本地资料):Readme、simple-shell.c、simple-execute.c;只读核对接口,没有把现有完成作答放入提示页。
  • HW01 教程(本地课件) p.2–4:参数传递与进程调用背景。具体接口约定以教师源码为准。
  • 原始文件保留本地;这里是练习引导,不是提交文件,也不代表这些自测已在你的当前实现上运行通过。