计算机系统实验shelllab,Csapp - Shelllab

shell lab

在尝试完成这个 shell lab 之前,先看看官方给了什么代码吧,一个是书上有的 shllex.c 另外还发现了神仙代码 shell.c ,这个shell.c在官方给的 code 页面中 complete set 中能找到,感觉这个很靠近我们要实现的结果。

Here is a tarfile that contains the complete set of source files, header files, and Makefiles used to produce the manuscript.

打算在此基础上更改。

然后搞清楚 jobs, bg, fg 的用法:

WriteUp

然后发现还有极其重要的 WriteUp 要读,读 pdf 文档摘取重要信息

大概函数长度

我们一共需要写7个函数,每个函数的大概长度有告知(包括注释):

eval: 70

builtin_cmd: 25

do_bgfg: 50

waitfg: 20

sigchild_handler: 80

sigint_handler: 15

sigtstp_handler: 15

Ctrl - C 导致 SIGIN, 终止 process, ctrl - z 导致 SIGSTP,暂停, SIGCONT 可以唤醒。

jobs: list

bg : stopped background job running in background

fg : stopped/running background job run in foreground

kill : terminate a job

别的提示

ctrl - c / ctrl - z 应该发到目前的 这个foreground job, 同时包括它的孩子,如果没有 foreground job, 则无效

每个 job 都有 pid 和 jid, jid 是 %5 的格式, pid 就是 5

bg/fg 这个 接收 pid 和 jid

Hints

熟悉 waitpid, kill, fork, execve, setpid, sigprocmask 函数和 waipid 中的参数 WUNTRACED/ WNOHANG

需要发送给整个foreground, 用 '-pid' 而非 'pid'

waitfg 和 sigchld_handler 建议如下处理方式: 在 waitfg 中,使用包含 sleep 的 loop; 在sigchld_handler 中,用一个 waitpid。 当然也说了别的方式也ok。比如在 waitfg 和 sigchld_handler 中都使用 waipid。

eval 中需要注意 race condtion的

fork 之后,执行之前,我们需要用 setpgid(0, 0)这个避免 ctrl-c 发送到每个process,包括shell.

测试

模仿改了代码之后,开始做一些测试,做测试参考了这里的办法,用了一个bash file

#! /bin/bash

for file in $(ls trace*)

do

./sdriver.pl -t $file -s ./tshref > tshref_$file

./sdriver.pl -t $file -s ./tsh > tsh_$file

done

for file in $(ls trace*)

do

diff tsh_$file tshref_$file > diff_$file

done

for file in $(ls diff_trace*)

do

echo $file " :"

cat $file

echo -e "-------------------------------------\n"

[x] trace01

[x] trace02

[x] trace03

[x] trace04

[x] trace05

[x] trace06

[x] trace07

[x] trace08

[x] trace09

[ ] trace10

[x] trace11

[x] trace12

[x] trace13

[ ] trace14

[ ] trace15

[x] trace16

遗憾的发现到 trace10, trace14, trace15 有不对的地方,都是在出现类似 trace10.txt 的这样的状况下出错,调用 fg %1, 然后测试 TSTP, 看之后的进程状态,应该是正确接收到了 SIGTSTP, 但是我们并没有打印出来,o(╯□╰)o

或者我尝试把打印的部分放到 sigtstp 中,但是发现会出现打印两次的问题,不知道这个是因为什么o(╯□╰)o,不管了

代码

所以最终代码长这样,还是没有让所有的例子全对 (⊙﹏⊙)b,然后里面借鉴了很多之前提到的地方:

eval

/*

* eval - Evaluate the command line that the user has just typed in

*

* If the user has requested a built-in command (quit, jobs, bg or fg)

* then execute it immediately. Otherwise, fork a child process and

* run the job in the context of the child. If the job is running in

* the foreground, wait for it to terminate and then return. Note:

* each child process must have a unique process group ID so that our

* background children don't receive SIGINT (SIGTSTP) from the kernel

* when we type ctrl-c (ctrl-z) at the keyboard.

*/

void eval(char *cmdline)

{

char *argv[MAXARGS]; /* argv for execve() */

int bg; /* should the job run in bg or fg */

pid_t pid; /* process id */

/* parse command line */

bg = parseline(cmdline, argv);

if (argv[0] == NULL)

return; /* ignore empty lines */

if(!strcmp(argv[0],"quit"))

exit(0); /* terminate shell */

sigset_t mask_all, mask_one, prev_one;

sigfillset(&mask_all);

sigemptyset(&mask_one);

sigaddset(&mask_one, SIGCHLD);

if (!builtin_cmd(argv)) {

// Block SIGCHLD

sigprocmask(SIG_BLOCK, &mask_one, &prev_one);

if ((pid = fork()) == 0) { /* child */

// Unblock SIGCHLD

sigprocmask(SIG_SETMASK, &prev_one, NULL);

setpgid(0,0);

/* Background jobs should ignore SIGINT (ctrl-c) */

/* and SIGTSTP (ctrl-z) */

if(bg){

Signal(SIGINT, SIG_IGN);

Signal(SIGTSTP, SIG_IGN);

}

if (execve(argv[0], argv, environ) < 0 ){

printf("%s: Command not found.\n", argv[0]);

fflush(stdout);

exit(0);

}

}

/* parent waits for foreground job to terminate or stop */

sigprocmask(SIG_BLOCK, &mask_all, NULL);

addjob(jobs, pid, (bg == 1 ? BG : FG), cmdline);

sigprocmask(SIG_SETMASK, &prev_one, NULL);

if (!bg)

waitfg(pid);

else

/* background job, we need to print out jid,pid,cmd */

printf("[%d] (%d) %s",pid2jid(pid), pid, cmdline);

}

return;

}

builtin_cmd

/*

* builtin_cmd - If the user has typed a built-in command then execute

* it immediately.

*/

int builtin_cmd(char **argv)

{

char *cmd = argv[0];

/* job command */

if (!strcmp(cmd,"jobs")){

listjobs(jobs);

return 1;

}

/* bg and fg commands */

if (!strcmp(cmd, "bg") || !strcmp(cmd, "fg")) {

do_bgfg(argv);

return 1;

}

/* ignore singleton & */

if (!strcmp(argv[0], "&")){

return 1;

}

/* not a builtin command */

return 0;

}

do_bgfg

/*

* do_bgfg - Execute the builtin bg and fg commands

*/

void do_bgfg(char **argv)

{

char *cmd = argv[0];

int pid;

struct job_t *jobp;

/* ignore command if no argument */

if (argv[1] == NULL) {

printf("%s: command requires PID or %%jobid argument\n", cmd);

return;

}

if (argv[1][0] == '%'){

char *t = &argv[1][1];

int jid = atoi(t);

jobp = getjobjid(jobs, jid);

if (jobp == NULL){

printf("%s: No such job\n",argv[1]);

return;

}

pid = jobp->pid;

} else {

pid = atoi(argv[1]);

if (pid == 0) {

printf("%s argument must be a PID or %%jobid\n",cmd);

return;

}

jobp = getjobpid(jobs, pid);

if(jobp == NULL){

printf("(%d): No such process\n", pid);

return;

}

}

if (!strcmp(cmd,"bg")) {

kill(-pid, SIGCONT);

updatejob(jobs, pid, BG);

printf("[%d] (%d) %s", pid2jid(pid), pid, jobp->cmdline);

}

if(!strcmp(cmd,"fg")) {

kill(-pid,SIGCONT);

updatejob(jobs, pid, FG);

waitfg(pid);

}

return;

}

waitfg

/*

* waitfg - Block until process pid is no longer the foreground process

*/

void waitfg(pid_t pid)

{

struct job_t *job = getjobpid(jobs, pid);

while(job->state == FG){

sleep(1);

}

return;

}

sigchld_handler

/*

* sigchld_handler - The kernel sends a SIGCHLD to the shell whenever

* a child job terminates (becomes a zombie), or stops because it

* received a SIGSTOP or SIGTSTP signal. The handler reaps all

* available zombie children, but doesn't wait for any other

* currently running children to terminate.

*/

void sigchld_handler(int sig)

{

pid_t pid;

int status;

if (verbose)

printf("sigchild_handler: entering \n");

while ((pid = waitpid(-1, &status, WUNTRACED | WNOHANG)) > 0){

/* FG job has stopped. Change its state in jobs list */

if (WIFSTOPPED(status)){

sprintf(sbuf,"Job [%d] (%d) stopped by signal %d", pid2jid(pid) ,pid , WSTOPSIG(status));

printf("%s\n",sbuf);

updatejob(jobs, pid, ST);

}

/*

* Reap any zombie jobs.

* The WNOHANG here is important, Without it, the

* handler would wait for all running or stopped BG jobs

* to terminate, during which time the shell would not

* be able to accept input.

*/

/* FG job has terminated. Remove it from jobs list */

else {

/* check if job was terminated by an uncaught signal */

if (WIFSIGNALED(status)){

sprintf(sbuf,"Job [%d] (%d) terminated by signal %d", pid2jid(pid),pid,WTERMSIG(status));

printf("%s\n",sbuf);

fflush(stdout);

}

sigset_t mask_all, prev_all;

sigfillset(&mask_all);

sigprocmask(SIG_BLOCK, &mask_all, &prev_all);

deletejob(jobs, pid);

sigprocmask(SIG_SETMASK, &prev_all, NULL);

if (verbose)

printf("sigchld_handler: job %d deleted\n",pid);

}

}

/*

* Check for normal loop termination.

* This is a little tricky. For our purposes,

* the waitpid loop terminates normally for one of

* two reasons: (a) there are no children left

* (pid == -1 and errno = ECHILD) or (b) there are

* still children left, but none of them are zombies (pid == 0).

*/

if (!((pid == 0) || (pid == -1 && errno == ECHILD)))

unix_error("sigchld_handler wait error");

if (verbose)

printf("sigchld_handler: exiting\n");

return;

}

sigint_handler

/*

* sigint_handler - The kernel sends a SIGINT to the shell whenver the

* user types ctrl-c at the keyboard. Catch it and send it along

* to the foreground job.

*/

void sigint_handler(int sig)

{

pid_t pid;

pid = fgpid(jobs);

if (pid) {

kill(-pid, sig);

//updatejob(jobs, pid, FG);

}

return;

}

sigtstp_handler

/*

* sigtstp_handler - The kernel sends a SIGTSTP to the shell whenever

* the user types ctrl-z at the keyboard. Catch it and suspend the

* foreground job by sending it a SIGTSTP.

*/

void sigtstp_handler(int sig)

{

pid_t pid;

pid = fgpid(jobs);

if (pid){

kill(-pid, SIGTSTP);

updatejob(jobs, pid, ST);

}

return;

}

updatejob

另外还补充了这个 updatejob 的函数,虽然感觉这个函数也就一句话可以完成的事|||

/* updatejob - update the state of a job with PID=pid */

void updatejob(struct job_t *jobs, pid_t pid, int state)

{

int i;

for (i = 0; i < MAXJOBS; i++) {

if (jobs[i].pid == pid) {

//DEBUG_PRINT("job %d updated state %d\n", pid, state);

jobs[i].state = state;

return;

}

}

printf("Job %d not found\n", pid);

return;

}

结束

🔚, 这个 lab 还是很难的,而且是看了各种参考最终拼凑出来,而且也不全对的状况,希望哪天能解决遗留的这个问题吧。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值