说明:本次实验是csapp的最后一个实验,其目标是自己实现一个shell以控制进程
—主要任务—
修改tsh.c文件补全以下七个函数:
- eval:解析命令行语句并运行进程
- builtin_cmd:判断是否是内建指令
- do_bgfg:执行 bg < job > 和 fg < job > 内置命令
- waitfg:等待 pid 进程不再是前台进程
- sigchld_handler:回收所有可获得的僵死子进程
- sigtstp_handler:响应 SIGTSTP(ctrl-z) 信号
- sigint_handler:响应 SIGINT(ctrl-c) 信号
—文件概览—
其中已经提供给我们的函数有:
- parseline - Parse the command line and build the argv array.
- clearjob - Clear the entries in a job struct
- initjobs - Initialize the job list
- maxjid - Returns largest allocated job ID
- addjob - Add a job to the job list
- deletejob - Delete a job whose PID=pid from the job list
- fgpid - Return PID of current foreground job, 0 if no such job
- getjobpid - Find a job (by PID) on the job list
- getjobjid - Find a job (by JID) on the job list
- pid2jid - Map process ID to job ID
- listjobs - Print the job list
—设计思路—
1.eval
(CSAPP中文版教材P525有提供部分代码)
满足以下要求:
- 内建命令(quit, jobs, bg, fg):立刻执行
- 否则:创建一个子进程,在子进程的上下文环境中运行
代码实现思路:
- 解析命令判断是否是内建语句;
- 如果不是,创建子进程
- 判断前后台运行:前台,等待子进程终止;后台,输出提示语句,格式参照trace4运行结果
注意:
- 每个子进程必须拥有自己的进程组id,以便向子进程组发送信号。否则,所有的子进程与tsh shell进程为同一个进程组,发送信号时,前后台的子进程均会收到。—— 实现:在fork()之后的子进程中setpgid(0,0)
setpgid(pid_t pid, pid_t pgid):将进程pid的进程组改为pgid。如果pid是0,那么就是用当前进程的pid,如果pgid是0,那么就用pid。
-
在fork()新进程前后要阻塞SIGCHLD信号,防止出现竞争的同步错误。否则,可能会出现子进程先运行结束从jobs中删除,而deletejob什么都不做,因为父进程还没有把子进程添加到列表中;然后再执行父进程,调用addjob错误地把不存在的子进程添加到作业列表中,永远也不会被删除。(CSAPP中文版教材P541)
-
子进程继承了父进程的被阻塞集合,在调用execve之前,要先小心解除子进程中阻塞的SIGCHID信号。
void eval(char *cmdline)
{
char *argv[MAXLINE];/* Argument list execve() */
char buf[MAXLINE]; /* Holds modified command line */
int bg; /* Should the job run in bg or fg? */
pid_t pid;
sigset_t mask_one;
strcpy(buf,cmdline);
bg=parseline(buf,argv);
if(argv[0]==NULL)
return; /* Ignore empty lines */
if(!builtin_cmd(argv)){
/* Child runs user job */
sigemptyset(&mask_one);
sigaddset(&mask_one,SIGCHLD);
Sigprocmask(SIG_BLOCK,&mask,NULL);/* Block SIGCHLD */
if((pid=fork())==0){
sigprocmask(SIG_UNBLOCK,&mask_one,NULL);/* Unblock SIGCHID */
setpgid(0,0);
if(execve(argv[0],argv,environ)<0){
printf("%s: Command not found\n",argv[0]);
exit(0);
}
}
/* Parent waits for foreground job to terminate */
addjob(jobs,pid,bg?BG:FG,cmdline);
Sigprocmask(SIG_UNBLOCK,&mask,NULL);/* Unblock SIGCHID */
bg?printf("[%d] (%d) %s",pid2jid(pid),pid,cmdline):waitfg(pid);
}
return;
}
2.builtin_cmd
判断命令行语句是否是内建指令quit, jobs, bg, fg
- 是:立即执行(对于单独的“&”指令直接无视),返回1
- 不是:返回0
int builtin_cmd(char **argv)
{
if(!strcmp(argv[0],"quit"))
exit(0);
if(!strcmp(argv[0],"bg")||!strcmp(argv[0],"fg")){
do_bgfg(argv);
return 1;
}
if(!strcmp(argv[0],"jobs")){