目标
补全tsh.c中剩余的代码:
-
void eval(char *cmdline):解析并执行命令。 -
int builtin_cmd(char **argv):检测命令是否为内置命令quit、fg、bg、jobs。 -
void do_bgfg(char **argv):实现bg、fg命令。 -
void waitfg(pid_t pid):等待前台命令执行完成。 -
void sigchld_handler(int sig):处理SIGCHLD信号,即子进程停止或终止。 -
void sigint_handler(int sig):处理SIGINT信号,即来自键盘的中断ctrl-c。 -
void sigtstp_handler(int sig):处理SIGTSTP信号,即终端停止信号ctrl-z。
使用make testn用来测试你编写的shell执行第n组测试数据的输出。
使用make rtestn用来测试参考shell程序第n组测试数据的输出(共16组测试数据)。
tshref.out包含参考shell程序的所有测试数据的输出结果,先看完该文件了解命令格式在开始编码。
说明
可用辅助函数:
-
int parseline(const char *cmdline,char **argv):获取参数列表char **argv,返回是否为后台运行命令(true)。 -
void clearjob(struct job_t *job):清除job结构。 -
void initjobs(struct job_t *jobs):初始化jobs链表。 -
void maxjid(struct job_t *jobs):返回jobs链表中最大的jid号。 -
int addjob(struct job_t *jobs,pid_t pid,int state,char *cmdline):在jobs链表中添加job -
int deletejob(struct job_t *jobs,pid_t pid):在jobs链表中删除pid的job。 -
pid_t fgpid(struct job_t *jobs):返回当前前台运行job的pid号。 -
struct job_t *getjobpid(struct job_t *jobs,pid_t pid):返回pid号的job。 -
struct job_t *getjobjid(struct job_t *jobs,int jid):返回jid号的job。 -
int pid2jid(pid_t pid):将pid号转化为jid。 -
void listjobs(struct job_t *jobs):打印jobs。 -
void sigquit_handler(int sig):处理SIGQUIT信号。
eval
- 为避免子进程在未加入到
jobs链表中就发送信号(SIGINT、SIGCHLD、SIGTSTP)去处理jobs链表(竞争),在子进程创建前需要将SIGCHLD阻塞,在加入jobs链表后解锁该阻塞。 -
fork后的子进程会继承父进程的信号屏蔽字并且在exec后仍会继承,所以需要在执行可执行文件前复原信号屏蔽字。 -
fork后的子进程会继承父进程的信号处理设置,而exec后不会继承。 - 参考《深入理解计算机第三版》
p543,如何解决竞争问题。
void eval(char *cmdline)
{
char *argv[MAXARGS];
char buf[MAXLINE];
int bg;
pid_t pid;
strcpy(buf,cmdline);
bg = parseline(buf,argv);
if(argv[0] == NULL)
return;
if(!builtin_cmd(argv)){
sigset_t mask_all,mask_one,prev;
Sigfillset(&mask_all);
Sigemptyset(&mask_one);
Sigaddset(&mask_one,SIGCHLD);
Sigprocmask(SIG_BLOCK,&mask_one,&prev);
if((pid=Fork()) == 0){
setpgid(0,0);
Sigprocmask(SIG_SETMASK,&prev,NULL);
if(execve(argv[0],argv,environ) < 0){
printf("%s: Command not found.\n",argv[0]);
exit(0);
}
}
int state = bg ? BG:FG;
//parent
Sigprocmask(SIG_BLOCK,&mask_all,NULL);
addjob(jobs,pid,state,cmdline);
Sigprocmask(SIG_SETMASK,&prev,NULL);
if(!bg){
waitfg(pid);
}else{
printf("[%d] (%d) %s",pid2jid(pid),pid,cmdline);
}
}
return;
}
builtin_cmd
- 参考《深入理解计算机第三版》
p525。
int builtin_cmd(char **argv)
{
if(!strcmp(argv[0],"quit"))
exit(0);
else if(!strcmp(argv[0],"jobs")){
listjobs(jobs);
return 1;
}else if(!strcmp(argv[0],"bg") || !strcmp(argv[0],"fg")){
do_bgfg(argv);
return 1;
}
return 0; /* not a builtin command */
}
do_bgfg
void do_bgfg(char **argv)
{
if(argv[1] == NULL){
printf("%s command requires PID or %%jobid argument\n",argv[0]);
return;
}
int bg = !strcmp(argv[0],"bg");
struct job_t *job_ptr;
pid_t pid;
int jid;
if(sscanf(argv[1],"%d",&pid) > 0){
// pid
job_ptr = getjobpid(jobs,pid);
if(job_ptr == NULL || job_ptr->state == UNDEF){
printf("(%d): No such process\n",pid);
return;
}
}else if(sscanf(argv[1],"%%%d",&jid) > 0){
// jid
job_ptr = getjobjid(jobs,jid);
if(job_ptr == NULL || job_ptr->state == UNDEF){
printf("%%%d: No such job\n",jid);
return;
}
}else{
printf("%s: argument must be a PID or %%jobid\n",argv[0]);
return;
}
// get the job_ptr;
if(bg){
printf("[%d] (%d) %s",job_ptr->jid,job_ptr->pid,job_ptr->cmdline);
job_ptr->state = BG;
kill(-job_ptr->pid,SIGCONT);
}else{
// "fg"
job_ptr->state = FG;
kill(-job_ptr->pid,SIGCONT);
waitfg(job_ptr->pid);
}
return;
}
waitfg
- 使用循环检查,简单实现。
- 参考《深入理解计算机第三版》
p545,显示等待信号。
void waitfg(pid_t pid)
{
while(pid == fgpid(jobs))
sleep(1);
return;
}
sigchld_handler
- 参考《深入理解计算机第三版》
p539,如何回收尽可能多的僵尸子进程。
void sigchld_handler(int sig)
{
int olderrno = errno;
sigset_t mask_all,prev;
pid_t pid;
int status;
Sigfillset(&mask_all);
while((pid = waitpid(-1,&status,WNOHANG|WUNTRACED)) > 0){
if(WIFEXITED(status)){
// normally exit
Sigprocmask(SIG_BLOCK,&mask_all,&prev);
deletejob(jobs,pid);
Sigprocmask(SIG_SETMASK,&prev,NULL);
}else if(WIFSIGNALED(status)){
// exit by signal
struct job_t *job_ptr = getjobpid(jobs,pid);
Sigprocmask(SIG_BLOCK,&mask_all,&prev);
printf("Job [%d] (%d) terminated by signal %d\n",job_ptr->jid,job_ptr->pid,WTERMSIG(status));
deletejob(jobs,pid);
Sigprocmask(SIG_SETMASK,&prev,NULL);
}else{ // stop
struct job_t *job_ptr = getjobpid(jobs,pid);
Sigprocmask(SIG_BLOCK,&mask_all,&prev);
printf("Job [%d] (%d) stopped by signal %d\n",job_ptr->jid,job_ptr->pid,WSTOPSIG(status));
job_ptr->state= ST;
Sigprocmask(SIG_SETMASK,&prev,NULL);
}
}
errno = olderrno;
return;
}
sigint_handler
- 最后两个函数,均向子进程发送信号。
void sigint_handler(int sig)
{
int olderrno = errno;
pid_t pid = fgpid(jobs);
if(pid != 0){
kill(-pid,SIGINT);
}
errno = olderrno;
return;
}
sigtstp_handler
void sigtstp_handler(int sig)
{
int olderrno = errno;
pid_t pid = fgpid(jobs);
if(pid != 0){
kill(-pid,SIGTSTP);
}
errno = olderrno;
return;
}