1. 实验概览:从零开始构建一个简易Shell
如果你用过Linux或者macOS的命令行,那你一定对Shell不陌生。它就像一个翻译官,把你输入的命令(比如ls -l)翻译成系统能听懂的语言,然后执行。CSAPP的Shell Lab,就是让你亲手打造一个这样的翻译官,而且是一个支持作业控制的增强版。听起来有点复杂?别怕,我当年第一次做这个实验时也是一头雾水,但跟着步骤走下来,你会发现它其实是一个把书本上抽象的“进程”、“信号”概念变得触手可及的绝佳机会。
这个实验的核心,是让你在tsh.c这个框架文件里,填充几个关键函数,最终得到一个能跑起来的、支持前台/后台任务切换、能响应Ctrl+C(中断)和Ctrl+Z(暂停)的微型Shell。它不需要你从零写所有代码,实验包已经提供了命令行解析、作业列表管理等辅助函数。你的主战场,集中在几个核心函数上:eval(命令评估)、builtin_cmd(内置命令处理)、do_bgfg(前后台切换)、waitfg(等待前台作业),以及三个信号处理函数。整个实验就像在搭积木,每完成一个函数,你的Shell就多一项能力。
我强烈建议你在动手前,至少把CSAPP教材的第八章“异常控制流”好好读一遍。不瞒你说,我第一次做的时候没认真看,直接开干,结果在信号处理那块卡了好久,反复调试才发现书里早就把解决方案讲清楚了。信号处理是这里的难点,也是精华所在,理解了它,你对操作系统如何管理进程会有全新的认识。
2. 核心战场:eval函数与进程创建的“竞争冒险”
eval函数是整个Shell的大脑。它的任务很简单:拿到用户输入的一行命令,判断该怎么做。如果是quit、jobs、bg、fg这种内置命令,就直接执行;如果是像/bin/ls这样的外部程序,就需要fork一个子进程去执行。
听起来挺直接,对吧?但这里藏着一个初学者最容易踩的“坑”,也是这个实验第一个需要你严肃对待的挑战:竞争条件。想象一下这个场景:父进程(你的Shell)刚刚fork出一个子进程,还没来得及把这个子进程的信息记录到自己的“任务清单”(作业列表)里,这个子进程就因为某种原因(比如命令错误)瞬间结束了。子进程结束会向父进程发送SIGCHLD信号,信号处理函数sigchld_handler被触发,它试图去“任务清单”里删除这个子进程——结果发现根本找不到!因为父进程还没来得及添加。这就导致了状态不一致。
怎么解决?核心思想就是:在“添加记录”这个关键操作完成前,先屏蔽掉可能打扰它的信号。具体到代码里,我们需要用到信号屏蔽字(signal mask)和sigprocmask函数

2万+

被折叠的 条评论
为什么被折叠?



