Skip to content
Deepcity's Blog
Go back

CMU-15213-ShellLab

在 GitHub 上编辑

Shell Lab

Declaration

本文使用了 AIGC 来提高效率,其中可能存在谬误,我已尽力检查并校对,但仍不保证完全准确,欢迎指正。

Steps

要求实现一个带有作业控制的Unix Shell程序,考虑并发,进程控制以及信号和信号的处理。

共计约300行代码

func-uncomplete

更加详细的要求在writeup中,我这里就不重复了。贴出本仓库放writeup的位置CMU-15213/resources at main · Deepcity/CMU-15213

在这里还包含了一个tshref的参考tsh,以及16个测试用例。通过跑trace可以测试自己的shell是否符合要求。详细方式是

# tshref 测试
make rtest<testid>
##tsh 测试
make test<testid>

对拍两者输出,详细请查看handout中Makefile文件

Code Policy

  1. 在处理信号时,使用sigsuspend来等待信号的到来,避免忙等待
  2. 在调用unix系统调用时,检查返回值以处理可能的错误

TEST

下面仅列出几个容易出现错误的tsh测试用例以及修复过程

TEST04

这里调度了一个./myspin 1&的后台程序,旨在看shell能否正确invoke后台程序,并且正确打印出[jid] (pid) cmd &的信息

注意看eval函数中对后台作业的处理部分

TEST05

对内建命令jobs的测试,要求打印出所有后台作业的信息

注意看builtin_cmd函数中对jobs的处理部分

TEST06

看是否能够进行前台作业的控制,这里调度了一个./myspin 4的前台程序,要求等待其执行完毕,并重定向输入输出。

注意exec_cmd函数中对前台作业的处理部分

TEST14

该测试是对fg,bg的分支测试。详见do_bgfg函数中对bgfg的处理部分。

QA

  1. 为什么在信号处理函数中要保存和恢复errno

    在信号处理函数中,可能会调用一些系统调用,这些调用可能会修改errno的值。如果不保存和恢复errno,可能会导致主程序中的错误处理逻辑出现问题。因此,在进入信号处理函数时保存当前的errno值,在退出时恢复它,可以确保主程序的错误处理逻辑不受影响。

  2. 为什么在信号处理函数中要阻塞所有信号?

    在信号处理函数中阻塞所有信号,可以防止在处理当前信号时被其他信号打断。这有助于确保信号处理函数的原子性,避免竞态条件和不一致状态的出现。通过使用sigprocmask函数,可以临时阻塞其他信号,确保当前信号处理完成后再恢复信号的处理。

  3. 什么时候应该使用负的PID参数调用kill函数?

    使用负的PID参数调用kill函数时,表示向一个进程组发送信号。具体来说,kill(-pid, sig)会将信号s发送给进程组ID为pid的所有进程。这在实现作业控制时非常有用,因为一个作业可能包含多个进程,使用负的PID可以方便地向整个作业发送信号,例如终止或停止整个作业。

  4. SIGCHLD, SIGINT, SIGTSTP 信号分别代表什么?

    • SIGCHLD:当子进程终止或停止时,父进程会收到这个信号。它通常用于通知父进程子进程的状态变化,以便父进程可以进行相应的处理,例如回收子进程资源。
    • SIGINT:这是一个中断信号,通常由用户通过键盘输入Ctrl-C触发。它用于请求终止当前正在运行的前台进程。
    • SIGTSTP:这是一个停止信号,通常由用户通过键盘输入Ctrl-Z触发。它用于请求暂停当前正在运行的前台进程,使其进入后台暂停状态。
  5. sigprocmask 函数的作用是什么?

    sigprocmask函数用于改变当前进程的信号屏蔽字。它可以用来阻塞或解除阻塞特定的信号,从而控制哪些信号可以被处理。通过使用sigprocmask,程序可以确保在关键代码段中不会被特定信号打断,从而避免竞态条件和不一致状态的出现。

  6. sigprocmask 函数的参数含义是什么?

    sigprocmask函数的参数通常包括三个部分:

    • how:指定如何修改信号屏蔽字。常见的取值有SIG_BLOCK(阻塞指定的信号)、SIG_UNBLOCK(解除阻塞指定的信号)和SIG_SETMASK(设置新的信号屏蔽字)。
    • set:一个指向sigset_t类型的指针,表示要阻塞或解除阻塞的信号集合。
    • oldset:一个指向sigset_t类型的指针,用于保存调用前的信号屏蔽字。如果不需要保存,可以传递NULL
  7. 为什么在信号处理函数中使用 sigfillset 函数?

    sigfillset函数用于初始化一个信号集合,将所有信号都添加到该集合中。在信号处理函数中使用sigfillset,可以方便地阻塞所有信号,确保在处理当前信号时不会被其他信号打断。这有助于保持信号处理的原子性,避免竞态条件和不一致状态的出现。

  8. shell中什么时候应该阻塞信号,原因是什么?

    在shell中,通常在处理关键代码段时需要阻塞信号,例如在修改作业列表或等待前台作业完成时。阻塞信号的原因是为了防止在这些关键操作过程中被其他信号打断,从而导致竞态条件和不一致状态的出现。通过阻塞信号,可以确保关键操作的原子性,保证shell的稳定性和正确性。

REF

  1. (41 封私信 / 83 条消息) CSAPP | Lab7-Shell Lab 深入解析 - 知乎

在 GitHub 上编辑
Share this post on:

上一篇
CMU-15213-MallocLab
下一篇
Ascend C 算子开发 Part5 PyTorch 算子调用与阶段总结