ShellLab
教训 一开始并没有仔细看题目, 没有按照关卡一步一步完善shell, 把什么父子进程竞争, 前台进程挂起shell, 作业添加删除………杂七杂八的东西一股脑全写上去, 最终导致的结果就是拿去运行了一下, 连最基本的quit指令都运行不了, 提示是段错误(核心已转储). 忙活两天还是个小丑.
所以还是得扎扎实实一步一步来. 先把书看明白了, 然后把实验说明书看完了(虽然是英文的, 但是还得看). 再来做这个shell lab.
第一关 不用做任何调整都是对的
第二关 测试quit指令, 直接在builtin_cmd里修改即可
测试一下, 是正确的
第三关 这一关是运行一个前台作业, 这就要求对shell建立起一个基本框架了.
我是在第四关的时候才想起来要写WP, 所以这里的代码没有粘贴下来, 但是大部分都是参考书中P525的代码. 这一道题的前台进程不用挂起shell也是正确的, 所以照抄应该也能过.
第四关 这一关是先运行一个前台进程(第一条指令用的是Linux的绝对路径, 一开始没看出来, 后来参考别人的实验博客才知道是运行一个打印字符串的前台进程), 然后再运行一个后台进程.
涉及到的知识点:
因为运行后台进程后要打印作业信息, 所以我们要添加和删除作业信息
前台进程需要挂起
父子进程之间的race
贴一下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 volatile pid_t hangpid;void eval (char *cmdline) { char *argv[MAXARGS]; char buf[MAXLINE]; int bg; pid_t pid; int bgjid; sigset_t mask_all_job, prev_all_job; sigset_t mask_hang, prev_hang; sigfillset(&mask_all_job); signal(SIGCHLD, sigchld_handler); strcpy (buf, cmdline); sigemptyset(&mask_hang); sigaddset(&mask_hang, SIGCHLD); bg = parseline(cmdline, argv); if (argv[0 ] == NULL ) return ; if (!builtin_cmd(argv)) { sigprocmask(SIG_BLOCK, &mask_hang, &prev_hang); if ((pid = fork()) == 0 ){ if (execve(argv[0 ], argv, environ) < 0 ){ printf ("Command not found.\n" ); exit (0 ); } } if (!bg){ hangpid = 0 ; sigprocmask(SIG_BLOCK, &mask_all_job, &prev_all_job); addjob(jobs, pid, 1 , cmdline); sigprocmask(SIG_SETMASK, &prev_all_job, NULL ); waitfg(hangpid); } else { sigprocmask(SIG_BLOCK, &mask_all_job, &prev_all_job); addjob(jobs, pid, 2 , cmdline); sigprocmask(SIG_SETMASK, &prev_all_job, NULL ); bgjid = pid2jid(pid); printf ("[%d] (%d) %s" , bgjid, pid, cmdline); } } return ; } void waitfg (pid_t pid) { sigset_t prev_hang; sigemptyset(&prev_hang); while (!hangpid) sigsuspend(&prev_hang); return ; } void sigchld_handler (int sig) { int olderrno = errno; sigset_t mask_all_job, prev_all_job; sigfillset(&mask_all_job); while ((hangpid = waitpid(-1 , NULL , 0 )) > 0 ) { sigprocmask(SIG_BLOCK, &mask_all_job, &prev_all_job); deletejob(jobs, hangpid); sigprocmask(SIG_SETMASK, &prev_all_job, NULL ); } if (errno != ECHILD) unix_error("waitpid error" ); errno = olderrno; } 中间有个错误没注意, 导致shell一直被挂起. 就是waitfg的形式参数设置为hangpid跟全局变量hangpid冲突.
最后检验一下
第五关 这一关是运行两个后台作业, 并用jobs内置指令打印出两个作业的信息. (但其实是运行了三个前台进程, 两个后台进程. 这个检验指令的机制应该是屏蔽掉了我们对shell的输入, 只保留了shell对我们的输出. 总而言之, 我们是可以把这个这些行为当成是运行两个后台作业, 然后打印作业信息.)
这道题的涉及的知识点:
waitpid的option的选择
如何使用fgpid来实现前台进程挂起, 而不是我们之前用到的全局变量hangpid
对于我个人而言(因为前面eval有漏洞), 我还要考虑到修改hangpid的影响
jobs内置指令的实现
关于前台进程挂起的实现, 在第四关是使用书上的方法, 但是在书中的情景没有题目那么复杂: 书中的挂起是单个前台作业, 而没有后台作业; 但是在我们实现第五关时却有两个后台作业.
修正 首先要知道我在哪里错了, 在我的调试中: 我的后台进程会使得shell挂起, 直到终止, 最终导致了我在使用jobs指令后什么都没有打印出来.
第一步修正 我首先发现的是sigchld_handler()
中的while ((hangpid = waitpid(-1, NULL, 0)) > 0)
的存在错误, 我们假设有两个后台进程在同一个进程组中, 当第一个后台进程终止时会发送SIGCHLD信号给内核, 然后进入sigchld_handler()
中, 回收完第一个后台进程后, 又会继续循环, 但是我们的第三个参数为0, 也就是默认行为, waitpid会等待第二个后台进程结束发送信号. 而我们这道题一个后台作业的实际实现是: 先运行一个前台作业打印我们的输入, 然后再运行我们的后台作业, 我认为这两个作业的父进程都是shell(虽然它们的作业组不一样), 这样当第一个前台进程结束的时候, waitpid会等待后台进程结束. 所以我们修改掉waitpid的默认行为为对停止和终止不等待WNOHANG | WUNTRACED
修改完了以后, 发现还是不行, 甚至连第五关的整个流程都跑不完了.
第二步修正 然后发现的是hangpid的问题, 经过检验发现实际实现用到的前台进程再改完了以后, 用于判断是否挂起的全局变量hangpid再最终判断处while (!hangpid)
一直都是0, 也就导致了前台进程一直挂起.原因是:
在我们修改waitpid默认行为前, sigchld_handler()
的实际处理方式是一直等待SIGCHLD信号, 直到不再有子进程; 也就是说返回值给hangpid只会是非零数(即便发生错误, 也返回的是负数而不是零), 这导致了不管怎样到了最后挂起都会被终止.
而我们将waitpid的默认行为修改后, sigchld_handler()
的处理方式是不等待了, 当处理完了一个僵死进程后, 如果后面没有更多的僵死进程了, 那就直接返回0; 再结合while循环, 也就是说到了循环的最后hangpid都会是零. 在题目的情境下, 挂起了第一个前台作业, 然后前台作业终止, 进入sigchld_handler()
中, 第一次循环hangpid为回收的pid, 然后回到循环检查, 第二次检查hangpid被赋值为0. 离开sigchld_handler()
函数, 来到挂起判断那里, 就一直挂起了
所以我们的第一部修正是没有问题的, 问题在于我们不应该使用hangpid来作为挂起判断依据.
我们将用fgpid()函数来代替hangpid判断是否需要挂起.(这里是参考了别人的博客的, 其实博客里的一些东西实验说明书有. 才发现读实验说明书是真的很重要!!!!!, 里面会教你使用实验室, 给你hint, 给你更详细的实验要求.)
fgpid()函数是直接查找你的作业列表里面没有前台作业, 如果有就挂起, 如果没有就取消挂起. 在sigchld_handler()
中我们就删除了第一个前台作业了, 所以使用这个就可以不用管waitpid的返回值是什么了.非常好用.
第三步修改 前面的两步修改其实已经完成了我们的主要工作了, 这个第三步修改主要是清理掉已经没有用处的hangpid, 详细的就是在sigchld_handler()
中把所有的hangpid换成局部变量pid, 然后删除掉在eval中的hangpid, 以及hangpid的定义
一下是我的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 void eval (char *cmdline) { char *argv[MAXARGS]; char buf[MAXLINE]; int bg; pid_t pid; int bgjid; sigset_t mask_all_job, prev_all_job; sigset_t mask_hang, prev_hang; sigfillset(&mask_all_job); signal(SIGCHLD, sigchld_handler); strcpy (buf, cmdline); sigemptyset(&mask_hang); sigaddset(&mask_hang, SIGCHLD); bg = parseline(cmdline, argv); if (argv[0 ] == NULL ) return ; if (!builtin_cmd(argv)){ sigprocmask(SIG_BLOCK, &mask_hang, &prev_hang); if ((pid = fork()) == 0 ){ if (execve(argv[0 ], argv, environ) < 0 ){ printf ("Command not found.\n" ); exit (0 ); } } if (!bg){ sigprocmask(SIG_BLOCK, &mask_all_job, &prev_all_job); addjob(jobs, pid, 1 , cmdline); sigprocmask(SIG_SETMASK, &prev_all_job, NULL ); waitfg(pid); } else { sigprocmask(SIG_BLOCK, &mask_all_job, &prev_all_job); addjob(jobs, pid, 2 , cmdline); sigprocmask(SIG_SETMASK, &prev_all_job, NULL ); bgjid = pid2jid(pid); printf ("[%d] (%d) %s" , bgjid, pid, cmdline); } } return ; } void waitfg (pid_t pid) { sigset_t prev_hang; sigemptyset(&prev_hang); while (pid == fgpid(jobs)) sigsuspend(&prev_hang); return ; } void sigchld_handler (int sig) { int status; pid_t pid; int olderrno = errno; sigset_t mask_all_job, prev_all_job; sigfillset(&mask_all_job); while ((pid = waitpid(-1 , &status, WNOHANG | WUNTRACED)) > 0 ){ sigprocmask(SIG_BLOCK, &mask_all_job, &prev_all_job); deletejob(jobs, pid); sigprocmask(SIG_SETMASK, &prev_all_job, NULL ); } errno = olderrno; }
最终的检验
第六关 这一关是用Ctrl + c(对应的信号是SIGINT)来结束一个前台作业(注意, 是前台!!!)
知识点考察:
如何分辨前台和后台作业: fgpid()
如何发送SIGINT信号: kill()
这一关还是比较直接的, 我们只用修改sigint_handler()
即可.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 void sigint_handler (int sig) { pid_t int_fg_pid; int int_jid; sigset_t mask_all_int, prev_all_int; sigfillset(&mask_all_int); sigprocmask(SIG_BLOCK, &mask_all_int, &prev_all_int); int_fg_pid = fgpid(jobs); int_jid = pid2jid(int_fg_pid); sigprocmask(SIG_SETMASK, &prev_all_int, NULL ); if (int_fg_pid) { printf ("Job [%d] (%d) terminated by signal 2\n" , int_jid, int_fg_pid); kill(-int_fg_pid, SIGINT); } return ; }
检验
第七关 要求是只将SIGINT信号发送给前台进程, 中断前台进程
知识点考察:
使用status状态来处理不同的情况(进程正常终止, 进程因SIGINT中止)
才知道上面的sigint处理函数是错的, printf要放在sigint函数中去
在进行第七关的修改的同时, 前面的eval()和sigchld处理函数中信号阻塞太乱了, 所以接着第六关的三个修改我们再次添加一个第四次修改.
此外还修改了sigchld_handler中的情况分类, 感觉在这里对sigchld_handler有了更加清楚的认识, 这个信号处理不仅仅只是针对一个信号, 而是对多种信号进行分类处理, 通过status来区别
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 void sigchld_handler (int sig) { int status; pid_t pid; int olderrno = errno; sigset_t mask_all, mask_prev; sigfillset(&mask_all); while ((pid = waitpid(-1 , &status, WNOHANG|WUNTRACED)) > 0 ) { sigprocmask(SIG_BLOCK, &mask_all, &mask_prev); if (WIFEXITED(status)) { deletejob(jobs, pid); } if (WIFSIGNALED(status)) { printf ("Job [%d] (%d) terminated by signal 2\n" , pid2jid(pid), pid); deletejob(jobs, pid); } sigprocmask(SIG_SETMASK, &mask_prev, NULL ); } errno = olderrno; } void eval (char *cmdline) { char *argv[MAXARGS]; char buf[MAXLINE]; int bg; pid_t pid; sigset_t mask_all, mask, mask_prev; sigemptyset(&mask); sigaddset(&mask, SIGCHLD); sigfillset(&mask_all); strcpy (buf, cmdline); bg = parseline(buf, argv); if (argv[0 ] == NULL ) return ; if (!builtin_cmd(argv)) { sigprocmask(SIG_BLOCK, &mask, &mask_prev); if ((pid = fork()) == 0 ) { sigprocmask(SIG_SETMASK, &mask_prev, NULL ); setpgid(0 , 0 ); if (execve(argv[0 ], argv, environ) < 0 ) { printf ("Command not found.\n" ); exit (0 ); } } if (!bg) { sigprocmask(SIG_BLOCK, &mask_all, NULL ); addjob(jobs, pid, 1 , cmdline); sigprocmask(SIG_SETMASK, &mask, NULL ); waitfg(pid); } else { sigprocmask(SIG_BLOCK, &mask_all, NULL ); addjob(jobs, pid, 2 , cmdline); sigprocmask(SIG_SETMASK, &mask, NULL ); printf ("[%d] (%d) %s" , pid2jid(pid), pid, cmdline); } sigprocmask(SIG_SETMASK, &mask_prev, NULL ); } return ; } void sigint_handler (int sig) { pid_t int_fg_pid = fgpid(jobs); if (int_fg_pid) { kill(-int_fg_pid, SIGINT); } return ; }
第八关 要求: 使用SIGSTP信号只(!!!!)让前台作业停止
考察知识点:
前台后台作业的区别
sigtstp_handler的编写
WIFSTOPPED(status)来区别信号情况
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 void sigchld_handler (int sig) { int status; pid_t pid; int olderrno = errno; sigset_t mask_all, mask_prev; sigfillset(&mask_all); struct job_t *job ; while ((pid = waitpid(-1 , &status, WNOHANG|WUNTRACED)) > 0 ) { sigprocmask(SIG_BLOCK, &mask_all, &mask_prev); if (WIFEXITED(status)) { deletejob(jobs, pid); } if (WIFSIGNALED(status)) { printf ("Job [%d] (%d) terminated by signal 2\n" , pid2jid(pid), pid); deletejob(jobs, pid); } if (WIFSTOPPED(status)) { job = getjobpid(jobs, pid); job->state = ST; printf ("Job [%d] (%d) stopped by signal %d\n" , pid2jid(pid), pid, WSTOPSIG(status)); } sigprocmask(SIG_SETMASK, &mask_prev, NULL ); } errno = olderrno; } void sigtstp_handler (int sig) { pid_t pid = fgpid(jobs); if (pid) kill(-pid, SIGTSTP); return ; }
检验 实现的结果
实验室提供的结果
第九关 实现内置指令bg(讲挂起的进程转入后台运行)和fg(讲挂起的进程转入前台进行)
知识点: 就是重新编写biltin_cmd函数和do_bgfg函数
贴上代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 int builtin_cmd (char **argv) { sigset_t mask_all, mask_prev; sigfillset(&mask_all); if (!strcmp (argv[0 ], "quit" )) { exit (0 ); } if (!strcmp (argv[0 ],"jobs" )){ sigprocmask(SIG_BLOCK, &mask_all, &mask_prev); listjobs(jobs); sigprocmask(SIG_SETMASK, &mask_prev, NULL ); return 1 ; } if (!strcmp (argv[0 ], "bg" ) || !strcmp (argv[0 ], "fg" )) { do_bgfg(argv); return 1 ; } return 0 ; } void do_bgfg (char **argv) { int jid; struct job_t *job ; if (!strcmp (argv[0 ], "bg" )) { jid = atoi(&argv[1 ][1 ]); job = getjobjid(jobs, jid); job->state = BG; kill(-(job->pid), SIGCONT); printf ("[%d] (%d) %s" , jid, job->pid, job->cmdline); } if (!strcmp (argv[0 ], "fg" )) { jid = atoi(&argv[1 ][1 ]); job = getjobjid(jobs, jid); job->state = FG; waitfg(job->pid); kill(-(job->pid), SIGCONT); printf ("[%d] (%d) %s" , jid, job->pid, job->cmdline); } return ; }
检验
第十关 fg内置指令的更加全面的实现:
将挂起的进程转到前台运行(上一个关卡已经实现了)
讲一个后台运行的进程转到前台
而挂起的进程跟一个后台运行的进程最直观(对于shell来说)的不同就是其结构体成员的state不同, 所以我们的根据state再次分情况讨论即可.关于fg的第一个情况的处理有错误的地方: 1.waitfg要放在kill后面, 否则进程会先挂起shell, 然后再发送kill的时候已经发送不了了, 因为已经挂起了, shell就会卡死.2. fg不用printf打印
贴一下代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 void do_bgfg (char **argv) { int jid; struct job_t *job ; if (!strcmp (argv[0 ], "bg" )) { jid = atoi(&argv[1 ][1 ]); job = getjobjid(jobs, jid); job->state = BG; kill(-(job->pid), SIGCONT); printf ("[%d] (%d) %s" , jid, job->pid, job->cmdline); } if (!strcmp (argv[0 ], "fg" )) { jid = atoi(&argv[1 ][1 ]); job = getjobjid(jobs, jid); if (job->state == ST) { job->state = FG; kill(-(job->pid), SIGCONT); waitfg(job->pid); } if (job->state == BG) { job->state = FG; waitfg(job->pid); } } return ; }
检验
第十一关 这一关是发送SIGINT给前台进程组的每一个进程
检验 即便不做修改, 结果跟参考是一样
第十二关 跟上面差不多, 不做修改也可以过
第十三关 跟上面一样, 不做修改也可以通过
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 这个是tsh的: ./sdriver.pl -t trace13.txt -s ./tsh -a "-p" # # trace13.txt - Restart every stopped process in process group # tsh> ./mysplit 4 Job [1 ] (9578 ) stopped by signal 20 tsh> jobs [1 ] (9578 ) Stopped ./mysplit 4 tsh> /bin/ps a PID TTY STAT TIME COMMAND 1214 tty2 Ssl+ 0 :00 /usr/libexec/gdm-wayland-session env GNOME_SHELL_SESSION_MODE=ubuntu /usr/bin/gnome-session --session=ubuntu 1229 tty2 Sl+ 0 :00 /usr/libexec/gnome-session-binary --systemd --session=ubuntu 4709 pts/0 Ss 0 :00 bash 9084 pts/0 S 0 :00 ./tsh -p 9086 pts/0 T 0 :00 ./myspin 4 9156 pts/0 S 0 :00 ./tsh -p 9158 pts/0 T 0 :00 ./myspin 4 9573 pts/0 S+ 0 :00 make test13 9574 pts/0 S+ 0 :00 /bin/sh -c ./sdriver.pl -t trace13.txt -s ./tsh -a "-p" 9575 pts/0 S+ 0 :00 /usr/bin/perl ./sdriver.pl -t trace13.txt -s ./tsh -a -p 9576 pts/0 S+ 0 :00 ./tsh -p 9578 pts/0 T 0 :00 ./mysplit 4 9579 pts/0 T 0 :00 ./mysplit 4 9582 pts/0 R 0 :00 /bin/ps a tsh> fg %1 tsh> /bin/ps a PID TTY STAT TIME COMMAND 1214 tty2 Ssl+ 0 :00 /usr/libexec/gdm-wayland-session env GNOME_SHELL_SESSION_MODE=ubuntu /usr/bin/gnome-session --session=ubuntu 1229 tty2 Sl+ 0 :00 /usr/libexec/gnome-session-binary --systemd --session=ubuntu 4709 pts/0 Ss 0 :00 bash 9084 pts/0 S 0 :00 ./tsh -p 9086 pts/0 T 0 :00 ./myspin 4 9156 pts/0 S 0 :00 ./tsh -p 9158 pts/0 T 0 :00 ./myspin 4 9573 pts/0 S+ 0 :00 make test13 9574 pts/0 S+ 0 :00 /bin/sh -c ./sdriver.pl -t trace13.txt -s ./tsh -a "-p" 9575 pts/0 S+ 0 :00 /usr/bin/perl ./sdriver.pl -t trace13.txt -s ./tsh -a -p 9576 pts/0 S+ 0 :00 ./tsh -p 9585 pts/0 R 0 :00 /bin/ps a 这个是tshref的: make rtest13 ./sdriver.pl -t trace13.txt -s ./tshref -a "-p" # # trace13.txt - Restart every stopped process in process group # tsh> ./mysplit 4 Job [1 ] (9591 ) stopped by signal 20 tsh> jobs [1 ] (9591 ) Stopped ./mysplit 4 tsh> /bin/ps a PID TTY STAT TIME COMMAND 1214 tty2 Ssl+ 0 :00 /usr/libexec/gdm-wayland-session env GNOME_SHELL_SESSION_MODE=ubuntu /usr/bin/gnome-session --session=ubuntu 1229 tty2 Sl+ 0 :00 /usr/libexec/gnome-session-binary --systemd --session=ubuntu 4709 pts/0 Ss 0 :00 bash 9084 pts/0 S 0 :00 ./tsh -p 9086 pts/0 T 0 :00 ./myspin 4 9156 pts/0 S 0 :00 ./tsh -p 9158 pts/0 T 0 :00 ./myspin 4 9586 pts/0 S+ 0 :00 make rtest13 9587 pts/0 S+ 0 :00 /bin/sh -c ./sdriver.pl -t trace13.txt -s ./tshref -a "-p" 9588 pts/0 S+ 0 :00 /usr/bin/perl ./sdriver.pl -t trace13.txt -s ./tshref -a -p 9589 pts/0 S+ 0 :00 ./tshref -p 9591 pts/0 T 0 :00 ./mysplit 4 9592 pts/0 T 0 :00 ./mysplit 4 9595 pts/0 R 0 :00 /bin/ps a tsh> fg %1 tsh> /bin/ps a PID TTY STAT TIME COMMAND 1214 tty2 Ssl+ 0 :00 /usr/libexec/gdm-wayland-session env GNOME_SHELL_SESSION_MODE=ubuntu /usr/bin/gnome-session --session=ubuntu 1229 tty2 Sl+ 0 :00 /usr/libexec/gnome-session-binary --systemd --session=ubuntu 4709 pts/0 Ss 0 :00 bash 9084 pts/0 S 0 :00 ./tsh -p 9086 pts/0 T 0 :00 ./myspin 4 9156 pts/0 S 0 :00 ./tsh -p 9158 pts/0 T 0 :00 ./myspin 4 9586 pts/0 S+ 0 :00 make rtest13 9587 pts/0 S+ 0 :00 /bin/sh -c ./sdriver.pl -t trace13.txt -s ./tshref -a "-p" 9588 pts/0 S+ 0 :00 /usr/bin/perl ./sdriver.pl -t trace13.txt -s ./tshref -a -p 9589 pts/0 S+ 0 :00 ./tshref -p 9598 pts/0 R 0 :00 /bin/ps a
第十四关 这道题是针对不同的输入错误情况, shell能正确识别并指出错误
主要是情况的分类, 参考了博客: https://blog.csdn.net/qq_45475106/article/details/111766734
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 void do_bgfg (char **argv) { int jid; struct job_t *job ; if (!strcmp (argv[0 ], "bg" )) { if (argv[1 ] == NULL ) { printf ("bg command requires PID or %%jobid argument\n" ); return ; } jid = atoi(&argv[1 ][1 ]); job = getjobjid(jobs, jid); job->state = BG; kill(-(job->pid), SIGCONT); printf ("[%d] (%d) %s" , jid, job->pid, job->cmdline); } if (!strcmp (argv[0 ], "fg" )) { if (argv[1 ] = NULL ) { printf ("fg command requires PID or %%jobid argument\n" ); return ; } else if (argv[1 ][0 ] == '%' ){ jid = atoi(&argv[1 ][1 ]); job = getjobjid(jobs, jid); if (job == NULL ){ printf ("%%%d: No such job\n" , jid); return ; } } else if (isdigit (argv[1 ][0 ])) { jid = atoi(argv[1 ]); job = getjobjid(jobs, jid); if (job == NULL ) { printf ("(%d): Nosuch process\n" , jid); return ; } } else { printf ("%s: argument must be a PID or %%jobid\n" , argv[0 ]); return ; } jid = atoi(&argv[1 ][1 ]); job = getjobjid(jobs, jid); if (job->state == ST) { job->state = FG; kill(-(job->pid), SIGCONT); waitfg(job->pid); } if (job->state == BG) { job->state = FG; waitfg(job->pid); } } return ; }
检验 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 tsh: crow@crow-virtual -machine:~/CSAPP/CSAPP/Shell Lab/shlab-handout (2 )$ make test14 ./sdriver.pl -t trace14.txt -s ./tsh -a "-p" # # trace14.txt - Simple error handling # tsh> ./bogus ./bogus: Command not found. tsh> ./myspin 4 & [1 ] (2124 ) ./myspin 4 & tsh> fg fg command requires PID or %jobid argument tsh> bg fg command requires PID or %jobid argument tsh> fg a fg: argument must be a PID or %jobid tsh> bg a bg: argument must be a PID or %jobid tsh> fg 9999999 (9999999 ): Nosuch process tsh> bg 9999999 (9999999 ): Nosuch process tsh> fg %2 %2 : No such job tsh> fg %1 Job [1 ] (2124 ) stopped by signal 20 tsh> bg %2 %2 : No such job tsh> bg %1 [1 ] (2124 ) ./myspin 4 & tsh> jobs [1 ] (2124 ) Running ./myspin 4 & rtsh: crow@crow-virtual -machine:~/CSAPP/CSAPP/Shell Lab/shlab-handout (2 )$ make rtest14 ./sdriver.pl -t trace14.txt -s ./tshref -a "-p" # # trace14.txt - Simple error handling # tsh> ./bogus ./bogus: Command not found tsh> ./myspin 4 & [1 ] (2146 ) ./myspin 4 & tsh> fg fg command requires PID or %jobid argument tsh> bg bg command requires PID or %jobid argument tsh> fg a fg: argument must be a PID or %jobid tsh> bg a bg: argument must be a PID or %jobid tsh> fg 9999999 (9999999 ): No such process tsh> bg 9999999 (9999999 ): No such process tsh> fg %2 %2 : No such job tsh> fg %1 Job [1 ] (2146 ) stopped by signal 20 tsh> bg %2 %2 : No such job tsh> bg %1 [1 ] (2146 ) ./myspin 4 & tsh> jobs [1 ] (2146 ) Running ./myspin 4 &
第十五关, 第十六关 不用修改直接过
警惕 后面的waitpid跟各种信号bgfg的混合使用还是有点混乱, 然后是后面的错误情况提示等等……