shelllab记录

shelllab

General Overview of Unix Shells

命令行是由空格分隔的ASCII文本单词序列。命令行中的第一个单词是内置命令的名称或可执行文件的路径名。剩下的字是命令行参数。如果第一个单词是一个内置命令,那么shell将立即在当前进程中执行该命令。否则,该词被假定为可执行程序的路径名。在这种情况下,shell派生一个子进程,然后在子进程的上下文中加载并运行程序。作为解释单个命令的结果而创建的子进程。
如果命令行以“&”结束,则作业在后台运行,这意味着shell不会在打印提示符并等待下一个命令行之前等待作业终止。否则,作业将在前台运行,这意味着shell将等待作业终止,然后再等待下一个命令行。因此,在任何时候,最多只能在前台运行一个作业。但是,可以在后台运行任意数量的作业。
e.g

tsh> jobs //执行内置的jobs命令
tsh> /bin/ls -l -d //runs the ls program in the foreground
/*int main(int argc, char *argv[])
• argc ==3,
• argv[0] == ‘‘/bin/ls’’,
• argv[1]== ‘‘-l’’,
• argv[2]== ‘‘-d’’.

tsh> /bin/ls -l -d & //runs the ls program in the background

Unix shell支持作业控制的概念,它允许用户在后台和前台之间来回移动作业,并更改作业中进程的状态(运行、停止或终止)。键入ctrl-c将导致向前台作业中的每个进程发送一个SIGINT信号。SIGINT的默认操作是终止进程。类似地,键入ctrl-z将导致向前台作业中的每个进程发送SIGTSTP信号。SIGTSTP的默认操作是将进程置于停止状态,直到它被SIGCONT信号唤醒。Unix shell还提供了支持作业控制的各种内置命令。

jobs: 列出正在运行和停止的后台作业。
bg <job>: 将已停止的后台作业更改为正在运行的后台作业。
fg <job>: 将已停止或正在运行的后台作业更改为正在运行的前台作业。
kill <job>: 终止作业。

tsh规范

  1. 提示符应该是字符串“tsh>”。
  2. 用户键入的命令行应该由一个名称和零个或多个参数组成,所有参数由一个或多个空格分隔。如果name是一个内置命令,那么tsh应该立即处理它并等待下一个命令行。否则,tsh应该假定名称是可执行文件的路径,它在初始子进程的上下文中加载并运行该文件(在此上下文中,术语job指的是初始子进程)。
  3. tsh不需要支持管道(|)或I/O重定向(<和>)。
  4. 键入ctrl-c (ctrl-z)应该会导致将SIGINT (SIGTSTP)信号发送到当前前台作业,以及该作业的任何后代(例如,它分叉的任何子进程)。如果没有前台作业,那么信号应该没有效果。
  5. 如果命令行以&符号结束,那么tsh应该在后台运行作业。否则,它应该在前台运行作业。
  6. 每个作业可以由进程ID (PID)或作业ID (JID)标识,后者是tsh分配的正整数。jid应该在命令行中用前缀’ % '表示。例如,“%5”表示JID 5,“5”表示PID 5。(我们已经为您提供了操作作业列表所需的所有例程。)
  7. tsh应该支持以下内置命令:
    quit命令终止shell。
    jobs命令列出了所有后台作业。
    通过发送一个SIGCONT信号,bg 命令重新启动,然后在后台运行它。参数可以是PID,也可以是JID。
    fg 命令通过发送SIGCONT信号重新启动,然后在前台运行它。参数可以是PID,也可以是JID。
  8. tsh应该收获它所有的zombie children。如果任何作业因为接收到未捕获的信号而终止,那么tsh应该识别此事件并打印一条带有作业的PID和违规信号描述的消息。

Checking Your Work

Reference solution. Linux可执行程序tshref是shell的参考解决方案。运行这个程序来解决关于shell应该如何运行的任何问题。您的shell应该发出与参考解决方案相同的输出(当然,pid除外,它会随着运行而变化)。
Shell driver. sdriver.pl program将shell作为子进程执行,根据跟踪文件的指示向它发送命令和信号,并捕获和显示shell的输出。
测试

unix> ./sdriver.pl -t trace01.txt -s ./tsh -a "-p" //(-a "-p"参数告诉shell不发出提示)
unix> make test01
//在参考程序上输入来对比
unix> ./sdriver.pl -t trace01.txt -s ./tshref -a "-p"
unix> make rtest01

hints

1,使用跟踪文件来指导shell的开发。从trace01开始。txt,确保您的shell生成与参考shell相同的输出。然后继续跟踪文件trace02。txt,等等。
2,waitpid、kill、fork、execve、setpgid和sigprocmask函数都非常方便。WUNTRACED和WNOHANG选项对waitpid也很有用。
在实现信号处理程序时,确保向整个前台进程组发送SIGINT和SIGTSTP信号,在kill函数的参数中使用“-pid”而不是“pid”。pl程序测试此错误。
3,分配任务的一个棘手部分是决定waitfg和sigchld处理函数之间的工作分配。我们建议采用以下方法:
- 在waitfg中,使用一个繁忙循环来处理sleep函数。
- 在sigchld处理程序中,只使用一个对waitpid的调用.
4,在eval中,父进程必须在派生子进程之前使用sigprocmask来阻止SIGCHLD信号,然后解除对这些信号的阻塞,在通过调用addjob将子进程添加到作业列表之后,再次使用sigprocmask。由于孩子继承了他们父母的阻塞向量,所以在执行新程序之前,孩子必须确保解除对SIGCHLD信号的阻塞。
5, 父进程需要以这种方式阻止SIGCHLD信号,以避免在父进程调用addjob之前,SIGCHLD处理程序捕获子进程(因此从作业列表中删除)的竞争条件。
6,诸如more、less、vi和emacs等程序对终端设置做了一些奇怪的事情。不要在您的shell中运行这些程序。坚持使用简单的基于文本的程序,如/bin/ls、/bin/ps和/bin/ echoc。
7,当您从标准的Unix shell运行您的shell时,您的shell正在前台进程组中运行。如果您的shell随后创建了一个子进程,在默认情况下,该子进程也将是前台进程组的成员。由于键入ctrl-c将向前景组中的每个进程发送一个SIGINT,因此键入ctrl-c将向您的shell以及您的shell创建的每个进程发送一个SIGINT,这显然是不正确的。
8,以下是解决方法:在fork之后,但在execve之前,子进程应该调用setpgid(0,0),这将把子进程放入一个新进程组,该组的组ID与子进程的PID相同。这确保前台进程组中只有一个进程,即您的shell。当您键入ctrl-c时,shell应该捕获生成的SIGINT,然后将其转发到适当的前台作业(或者更准确地说,转发到包含前台作业的进程组)。

函数

eval
如果用户请求一个内置命令(quit、jobs、bg或fg),则立即执行它。否则,派生子进程并在子进程的上下文中运行作业。如果作业在前台运行,等待它终止,然后返回。注意:每个子进程必须有一个惟一的进程组ID,这样当我们在键盘上键入ctrl-c (ctrl-z)时,我们的后台子进程就不会从内核接收SIGINT (SIGTSTP)。

parseline
用单引号括起来的字符被视为单个参数。如果用户请求了BG作业,则返回true;如果用户请求了FG作业,则返回false。

sigchld_handler
每当一个子作业终止(成为僵死作业),或者因为接收到SIGSTOP或SIGTSTP信号而停止时,内核就向shell发送一个SIGCHLD。处理程序获取所有可用的僵死子进程,但不等待当前正在运行的任何其他子进程终止。

sigint_handler
当用户在键盘上键入ctrl-c时,内核向shell发送一个SIGINT。捕获它并将其发送到前台作业。

sigtstp_handler
每当用户在键盘上键入ctrl-z时,内核都会向shell发送一个SIGTSTP。捕获它并通过发送一个SIGTSTP来挂起前台作业。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值