cvoid eval(char *cmdline)
{
char *argv[MAXARGS];
char buf[MAXLINE]; // command will be parsed and modified?
int bg; // whether it runs in background
pid_t pid;
// preprocess cmd line
strcpy(buf, cmdline);
bg = parseline(buf, argv); // convert the command into argv
if (argv[0] == NULL) // the line is empty, return
{
return;
}
// run external command
if (!builtin_cmd(argv))
{
if ((pid = fork()) == 0) // this is child
{
if (execve(argv[0], argv, environ) <0) // execute command failed
{
printf("%s: Command not found\n", argv[0]);
exit(0); // here only child exited
}
}
if (!bg) // run the process in foreground:
// wait for foreground job to terminate
{
int status;
if (waitpid(pid, &status, 0) <0) // if return -1, then waiting failed
{
unix_error("waitfg: waitpid error");
}
}
else
{
printf("%d %s", pid, cmdline);
}
}
return;
}
voideval(char*cmdline){char*argv[MAXARGS];charbuf[MAXLINE];// command will be parsed and modified?
intbg;// whether it runs in background
pid_tpid;// preprocess cmd line
strcpy(buf,cmdline);bg=parseline(buf,argv);// convert the command into argv
if(argv[0]==NULL)// the line is empty, return
{return;}// run external command
if(!builtin_cmd(argv)){if((pid=fork())==0)// this is child
{if(execve(argv[0],argv,environ)<0)// execute command failed
{printf("%s: Command not found\n",argv[0]);exit(0);// here only child exited
}}if(!bg)// run the process in foreground:
// wait for foreground job to terminate
{intstatus;if(waitpid(pid,&status,0)<0)// if return -1, then waiting failed
{unix_error("waitfg: waitpid error");}}else{printf("%d %s",pid,cmdline);}}return;}
c if (!builtin_cmd(argv)) // built-in command is done in `builtin_cmd`
{
if ((pid = fork()) == 0) // this is child
{
if (execve(argv[0], argv, environ) <0) // execute command failed
{
printf("%s: Command not found\n", argv[0]);
exit(0); // here only child exited
}
}
if (!bg) // run the process in foreground:
// wait for foreground job to terminate
{
int status;
if (waitpid(pid, &status, 0) <0) // if return -1, then waiting failed
{
unix_error("waitfg: waitpid error");
}
}
else
{
printf("%d %s", pid, cmdline);
}
}
if(!builtin_cmd(argv))// built-in command is done in `builtin_cmd`
{if((pid=fork())==0)// this is child
{if(execve(argv[0],argv,environ)<0)// execute command failed
{printf("%s: Command not found\n",argv[0]);exit(0);// here only child exited
}}if(!bg)// run the process in foreground:
// wait for foreground job to terminate
{intstatus;if(waitpid(pid,&status,0)<0)// if return -1, then waiting failed
{unix_error("waitfg: waitpid error");}}else{printf("%d %s",pid,cmdline);}}
先拿软柿子下手,将处理 jobs 命令的地方做好。tsh 已经为我们实现了 listjobs 函数,可以直接放到 builtin_cmd 中。注意 return 1 用来告诉 eval 已经找到了一个内置命令,否则会提示 “command not found”。
c if (strcmp(argv[0], "jobs") == 0) // t5: process jobs command
{
listjobs(jobs);
return 1; // this IS a builtin command, return 1 to notify
}
1
2
3
4
5
if(strcmp(argv[0],"jobs")==0)// t5: process jobs command
{listjobs(jobs);return1;// this IS a builtin command, return 1 to notify
}
但是不管怎么样,运行 jobs 都不会输出任何东西。毕竟在我们已有的代码里,既没有在任务开始时将其添加到任务列表,也没有在结束时将它移出。要做到将任务添加到任务列表,需要在 fork 后调用 addjob:
c printf("%s: Command not found\n", argv[0]);
exit(0); // here only child exited
}
}
addjob(jobs, pid, bg ? BG : FG, cmdline); // add the job to job list.
// When bg=1, state=2; bg=0, state=1. this way it's just elegant
if (!bg) // run the process in foreground:
1
2
3
4
5
6
7
printf("%s: Command not found\n",argv[0]);exit(0);// here only child exited
}}addjob(jobs,pid,bg?BG:FG,cmdline);// add the job to job list.
// When bg=1, state=2; bg=0, state=1. this way it's just elegant
if(!bg)// run the process in foreground:
上述代码的 addjob 中第三个参数 state 有三个取值,FG=1、BG=2、ST=3。虽然直接使用 bg+1 也是可行的方案,但这样使用三元运算符会更优雅更容易理解。
cvoid sigchld_handler(int sig)
{
pid_t pid;
int status;
while((pid=waitpid(-1,&status,WNOHANG|WUNTRACED))>0) // check if a child has become zombie, without wait
{
if(WIFEXITED(status))
{
deletejob(jobs,pid); // remove pid from job list
}
}
return;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
voidsigchld_handler(intsig){pid_tpid;intstatus;while((pid=waitpid(-1,&status,WNOHANG|WUNTRACED))>0)// check if a child has become zombie, without wait
{if(WIFEXITED(status)){deletejob(jobs,pid);// remove pid from job list
}}return;}
c if (!builtin_cmd(argv)) // built-in command is done in `builtin_cmd`
{
sigaddset(&mask, SIGCHLD);
sigprocmask(SIG_BLOCK, &mask, NULL); // 5. block SIGCHLD
if ((pid = fork()) == 0) // this is child
1
2
3
4
5
if(!builtin_cmd(argv))// built-in command is done in `builtin_cmd`
{sigaddset(&mask,SIGCHLD);sigprocmask(SIG_BLOCK,&mask,NULL);// 5. block SIGCHLD
if((pid=fork())==0)// this is child
然后,在子进程 execve 之前,恢复信号,修改如下:
c if ((pid = fork()) == 0) // this is child
{
sigprocmask(SIG_UNBLOCK, &mask, NULL); // 5. unblock SIGCHLD
if (execve(argv[0], argv, environ) <0) // execute command failed
1
2
3
4
if((pid=fork())==0)// this is child
{sigprocmask(SIG_UNBLOCK,&mask,NULL);// 5. unblock SIGCHLD
if(execve(argv[0],argv,environ)<0)// execute command failed
再然后,父进程 addjob 完毕后也要恢复:
c addjob(jobs, pid, bg ? BG : FG, cmdline); // add the job to job list.
// When bg=1, state=2; bg=0, state=1. this way it's just elegant
sigprocmask(SIG_UNBLOCK, &mask, NULL); // 5. unblock SIGCHLD
if (!bg) // run the process in foreground:
1
2
3
4
addjob(jobs,pid,bg?BG:FG,cmdline);// add the job to job list.
// When bg=1, state=2; bg=0, state=1. this way it's just elegant
sigprocmask(SIG_UNBLOCK,&mask,NULL);// 5. unblock SIGCHLD
if(!bg)// run the process in foreground:
cvoid sigint_handler(int sig)
{
pid_t pid = fgpid(jobs); // get pid of foreground job
if (kill(-pid, SIGINT) <0) // try to send SIGINT
{
unix_error("sigint error"); // failed
}
return;
}
1
2
3
4
5
6
7
8
9
voidsigint_handler(intsig){pid_tpid=fgpid(jobs);// get pid of foreground job
if(kill(-pid,SIGINT)<0)// try to send SIGINT
{unix_error("sigint error");// failed
}return;}
voidsigchld_handler(intsig){pid_tpid;intstatus;while((pid=waitpid(-1,&status,WNOHANG|WUNTRACED))>0)// check if a child has become zombie, without wait
{if(WIFEXITED(status)){deletejob(jobs,pid);// remove pid from job list
}if(WIFSIGNALED(status)){printf("Job [%d] (%d) terminated by signal %d\n",pid2jid(pid),pid,WTERMSIG(status));deletejob(jobs,pid);}}if(pid<0&&errno!=ECHILD){unix_error("waitpid error");}return;}
c sigprocmask(SIG_UNBLOCK, &mask, NULL); // 5. unblock SIGCHLD
setpgid(0, 0); // put the child process (0=current) into a new process group (0=current)
if (execve(argv[0], argv, environ) <0) // execute command failed
1
2
3
sigprocmask(SIG_UNBLOCK,&mask,NULL);// 5. unblock SIGCHLD
setpgid(0,0);// put the child process (0=current) into a new process group (0=current)
if(execve(argv[0],argv,environ)<0)// execute command failed
cvoid sigint_handler(int sig)
{
pid_t pid = fgpid(jobs); // get pid of foreground job
if (pid != 0) // if no foreground job (PID=0), do nothing (ONLY send to foreground)
{
if (kill(-pid, SIGINT) <0) // try to send SIGINT
{
unix_error("sigint error"); // failed
}
}
return;
}
1
2
3
4
5
6
7
8
9
10
11
12
voidsigint_handler(intsig){pid_tpid=fgpid(jobs);// get pid of foreground job
if(pid!=0)// if no foreground job (PID=0), do nothing (ONLY send to foreground)
{if(kill(-pid,SIGINT)<0)// try to send SIGINT
{unix_error("sigint error");// failed
}}return;}
c if (WIFSIGNALED(status)) // SIGINT, etc.
{
printf("Job [%d] (%d) terminated by signal %d\n", pid2jid(pid), pid, WTERMSIG(status));
deletejob(jobs, pid);
}
// 插入下面的部分
if (WIFSTOPPED(status)) // SIGTSTP, etc.
{
printf("Job [%d] (%d) stopped by signal %d\n", pid2jid(pid), pid, WSTOPSIG(status));
struct job_t *job = getjobpid(jobs, pid);
job->state = ST;
}
1
2
3
4
5
6
7
8
9
10
11
12
if(WIFSIGNALED(status))// SIGINT, etc.
{printf("Job [%d] (%d) terminated by signal %d\n",pid2jid(pid),pid,WTERMSIG(status));deletejob(jobs,pid);}// 插入下面的部分
if(WIFSTOPPED(status))// SIGTSTP, etc.
{printf("Job [%d] (%d) stopped by signal %d\n",pid2jid(pid),pid,WSTOPSIG(status));structjob_t*job=getjobpid(jobs,pid);job->state=ST;}
c char *id = argv[1], *end; // JID or PID
struct job_t *job;
int numid;
1
2
3
char*id=argv[1],*end;// JID or PID
structjob_t*job;intnumid;
然后检查参数是否存在。
c // extract job or process
if (id == NULL) // not specified
{
printf("%s command requires PID or %%jobid argument\n", argv[0]);
return;
}
1
2
3
4
5
6
// extract job or process
if(id==NULL)// not specified
{printf("%s command requires PID or %%jobid argument\n",argv[0]);return;}
对 JID 和 PID 的第一、二步处理是不尽相同的,斟酌再三还是分开处理为好。
首先是 JID 的情况。将 id 指针自增 1,是为了让指针指向第一个数字,然后使用 strtol 功能将其从字符串转为数字。在转换的过程中,end 会被设定为指向被转换的最后一个数字的下一个字符。正常情况下,JID/PID 并不应该包含除开头 % 号外的字符,所以 end 指向的应该是表示字符串结尾的 \0。具体可以参考 这个链接,但他的判断条件只能保证不以字符开头,如果遇到类似于 1a23 的字符串,仍然不会提示异常,并返回 1。虽然我这么做只是为了避免多遍历一遍字符串判断是否有其他字符,稍快一点,代码也优雅一点……
然后就是调用 getjobjid 得到 job 了,再加一个是否存在的判断。
c if (id[0] == '%') // this is a job
{
id++; // point to the number position
numid = strtol(id, &end, 10); // convert id char[] to integer
if (*end !='\0') // contains non-digit characters
{
printf("%s: argument must be a PID or %%jobid\n", argv[0]);
return;
}
job = getjobjid(jobs, numid); // try to get job
if (job == NULL)
{
printf("%%%d: No such job\n", numid);
return;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
if(id[0]=='%')// this is a job
{id++;// point to the number position
numid=strtol(id,&end,10);// convert id char[] to integer
if(*end!='\0')// contains non-digit characters
{printf("%s: argument must be a PID or %%jobid\n",argv[0]);return;}job=getjobjid(jobs,numid);// try to get job
if(job==NULL){printf("%%%d: No such job\n",numid);return;}}
对于 PID 的情况,不同的地方只在于没有自增,换了适用于 PID 的函数,以及提示信息改变而已。
c else // this is a process
{
numid = strtol(id, &end, 10);
if (*end !='\0')
{
printf("%s: argument must be a PID or %%jobid\n", argv[0]);
return;
}
job = getjobpid(jobs, numid); // try to get proc
if (job == NULL)
{
printf("(%d): No such process\n", atoi(id));
return;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
else// this is a process
{numid=strtol(id,&end,10);if(*end!='\0'){printf("%s: argument must be a PID or %%jobid\n",argv[0]);return;}job=getjobpid(jobs,numid);// try to get proc
if(job==NULL){printf("(%d): No such process\n",atoi(id));return;}}