做了csapp的shelllab,最开始真的是一点思路都没有。
1、要认真看给的文档,我是用谷歌翻译然后再中英文对照着看(不翻译我看的太慢,不中英对照看的会更蒙)。
2、要把书上的代码看懂。
做的时候对waitfg、还有3个处理函数很困惑。
1、
``void waitfg(pid_t pid)
{
while(fgpid(jobs))
{
sleep(0);
}
return;
}`
当子进程是前台进程,父进程tsh必须等到子进程完成后才能重新输入命令行(可以用ctrl+c、ctrl+z打断),父进程怎么接受到子进程完成这个消息?子进程是前台进程,子进程完成,说明前台进程就没有了,fgpid(jobs)函数刚好返回就是前台进程的pid,没有就返回0。
子进程完成后就会发送一个SIGCHLD信号给父进程tsh,父进程调用sigchld_handler处理函数,就用waitpid函数回收进程,同时删除jobs数组中的子进程。
2、`void sigint_handler(int sig)
{
pid_t pid;
pid = fgpid(jobs);
if(pid!=0)
{
kill(-pid,sig);
}
return;
}
tsh这个简单shell它是运行在shell上,当键入ctrl+c,内核会发送一个SIGINT给前台进程,运行tsh,shell的前台进程就是tsh,tsh就会收到SIGINT信号后,就运行sigint_handler处理函数,就把SIGINT信号发送给在tsh上运行的前台程序,所以用kill(-pid,sig)发送给前台进程组。
加pid !=0这个判断,是因为当tsh上没有运行前台进程,pid的值就是0,表明当前运行的是父进程tsh,更真正的shell一样,键入ctrl+c不会发生任何事,只会另起一行 user >。如果不加pid !=0,当在shell上./tsh后,键入ctrl+c会停在那,原因不知道?就好像是通过处理函数sigint_handler自己给自己发了个终止信号。
我的代码
void eval(char *cmdline)
{
sigset_t mask,prev_mask;
char *argv[MAXARGS];
int argc;
char buf[MAXLINE];
int bg;
pid_t pid;
strcpy(buf,cmdline);
bg=parseline(buf,argv,&argc);
if(argv[0]==NULL)
return;
if(!builtin_cmd(argv,argc))
{
//fork前要诸塞信号
sigemptyset(&mask);
sigfillset(&mask);
sigprocmask(SIG_BLOCK,&mask,&prev_mask);
if((pid=fork())==0)
{
sigprocmask(SIG_SETMASK,&prev_mask,NULL);
setpgid(0,0);
if(execve(argv[0],argv,environ)<0)
{
printf("%s :Command not found.\n",argv[0]);
return;
}
}
if(!bg)
{
addjob(jobs,pid,FG,cmdline);
sigprocmask(SIG_SETMASK,&prev_mask,NULL);
waitfg(pid);
}
else
{
addjob(jobs,pid,BG,cmdline);
sigprocmask(SIG_SETMASK,&prev_mask,NULL);
printf("[%d] (%d) %s",pid2jid(pid),pid,cmdline);
}
}
return;
}
/*
* parseline - Parse the command line and build the argv array.
*
* Characters enclosed in single quotes are treated as a single
* argument. Return true if the user has requested a BG job, false if
* the user has requested a FG job.
*/
int parseline(const char *cmdline, char **argv,int *a)
{
static char array[MAXLINE]; /* holds local copy of command line */
char *buf = array; /* ptr that traverses command line */
char *delim; /* points to first space delimiter */
int bg; /* background job? */
strcpy(buf, cmdline);
buf[strlen(buf)-1] = ' '; /* replace trailing '\n' with space */
while (*buf && (*buf == ' ')) /* ignore leading spaces */
buf++;
/* Build the argv list */
int argc = 0;
if (*buf == '\'') {
buf++;
delim = strchr(buf, '\'');
}
else {
delim = strchr(buf, ' ');
}
while (delim) {
argv[argc++] = buf;
*delim = '\0';
buf = delim + 1;
while (*buf && (*buf == ' ')) /* ignore spaces */
buf++;
if (*buf == '\'') {
buf++;
delim = strchr(buf, '\'');
}
else {
delim = strchr(buf, ' ');
}
}
argv[argc] = NULL;
if (argc == 0) /* ignore blank line */
return 1;
/* should the job run in the background? */
if ((bg = (*argv[argc-1] == '&')) != 0) {
argv[--argc] = NULL;
}
*a=argc;
return bg;
}
/*
* builtin_cmd - If the user has typed a built-in command then execute
* it immediately.
*/
int builtin_cmd(char **argv,int argc)
{
if(!strcmp(argv[0],"quit"))
{
exit(0);
}
if(!strcmp(argv[0],"jobs"))
{
listjobs(jobs);
return 1;
}
if(!strcmp(argv[0],"bg")||!strcmp(argv[0],"fg"))
{
do_bgfg(argv,argc);
return 1;
}
return 0; /* not a builtin command */
}
/*
* do_bgfg - Execute the builtin bg and fg commands
*/
void do_bgfg(char **argv,int argc)
{
if(argc!=2)
{
printf("%s command requires PID or %%jobid argument\n",argv[0]);
fflush(stdout);
return;
}
pid_t pid;
int jid;
struct job_t*cur_job;
char *cmd=argv[1];
if(cmd[0]=='%')
{
jid=atoi(&cmd[1]);
//atoi函数只要字符串不是整数,就返回0
if(jid==0&&strcmp(&cmd[1],"0"))
{
printf("%s argument must be a PID or %%jobid\n",argv[0]);
fflush(stdout);
return;
}
cur_job=getjobjid(jobs,jid);
if(cur_job==NULL)
{
printf("%%%d No such job\n",jid);
fflush(stdout);
return;
}
pid=cur_job->pid;
}
else
{
pid=atoi(&cmd[1]);
if(pid==0&&strcmp(&cmd[1],"0"))
{
printf("%s argument must be a PID or %%jobid\n",argv[0]);
fflush(stdout);
return;
}
cur_job=getjobpid(jobs,pid);
if(cur_job==NULL)
{
printf("(%d) No such process\n",pid);
fflush(stdout);
return;
}
}
kill(-pid,SIGCONT);
if(!strcmp(argv[0],"bg"))
{
cur_job->state=BG;
printf("[%d] (%d) %s",pid2jid(pid),pid,cur_job->cmdline);
fflush(stdout);
}
else
{
cur_job->state=FG;
printf("[%d] (%d) %s",pid2jid(pid),pid,cur_job->cmdline);
fflush(stdout);
waitfg(pid);
}
return;
}
/*
* waitfg - Block until process pid is no longer the foreground process
*/
void waitfg(pid_t pid)
{
while(fgpid(jobs))
{
sleep(0);
}
return;
}
/*****************
* Signal handlers
*****************/
/*
* 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)
{
int olderrno=errno;
int status;
sigset_t mask_all,prev_all;
pid_t pid;
sigfillset(&mask_all);
while((pid=waitpid(-1,&status,WNOHANG|WUNTRACED))>0)
{
sigprocmask(SIG_BLOCK,&mask_all,&prev_all);
if(WIFEXITED(status)){
deletejob(jobs,pid);
}
else if(WIFSTOPPED(status))
{
getjobpid(jobs,pid)->state=ST;
printf("Job [%d] [%d] stopped by signal %d\n",pid2jid(pid),pid,WSTOPSIG(status));
}
else if(WIFSIGNALED(status))
{
printf("Job [%d] [%d] terminated by signal %d\n",pid2jid(pid),pid,WTERMSIG(status));
deletejob(jobs,pid);
}
sigprocmask(SIG_SETMASK,&prev_all,NULL);
}
errno=olderrno;
return;
}
/*
* 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!=0)
{
kill(-pid,sig);
}
return;
}
/*
* 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 fg_pid = fgpid(jobs);
if(fg_pid!=0)
{
kill(-fg_pid,sig);
}
return;
}
在测试trace14和15的时候,总会多打一些。不知道为什么
./sdriver.pl -t trace14.txt -s ./tsh -a “-p”
trace14.txt - Simple error handling
tsh> ./bogus
./bogus :Command not found.
tsh> ./myspin 4 &
[1] (10101) ./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
(999999) No such process
tsh> bg 9999999
(999999) No such process
tsh> fg %2
%2 No such job
tsh> fg %1
[1] (10101) ./myspin 4 &
Job [1] [10101] stopped by signal 20
tsh> bg %2
%2 No such job
tsh> bg %1
[1] (10101) ./myspin 4 &
tsh> jobs
[1] (10101) Running ./myspin 4 &
tsh> ./myspin 4 &
[1] (10114) ./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
(999999) No such process
tsh> bg 9999999
(999999) No such process
tsh> fg %2
%2 No such job
tsh> fg %1
[1] (10114) ./myspin 4 &入代码片`