csapp:shell lab

实验要求

本次是要要求我们完成一个简单的shell程序。不过不是完成整个部分,只需要填充其中的部分函数即可。
eval:读取指令并产生子进程执行。
builtin_cmd:执行内置命令,bg、fg、quit、jobs。
do_bgfg:处理fg和bg操作。
waitfg:等待前台进程结束。‘
sigint_handler:将SIGINT信号发给前台进程组。
sigtstp_handler:将SIGTSTP信号发给前台进程组。
sigchld_handler:给子进程收尸,避免产生僵尸进程。
此外,在写代码之前还需要看懂tsh.c文件中的其他函数功能,便于在编写过程中调用。

代码

eval
void eval(char *cmdline) 
{
    char *argv[MAXARGS];
    char buf[MAXLINE];
    int bg;
    sigset_t prev;
    sigset_t mask;
    pid_t pid;
    sigprocmask(SIG_BLOCK,NULL,&mask);
    sigaddset(&mask,SIGCHLD);
    strcpy(buf,cmdline);
    bg=parseline(buf,argv);//内置函数,前台任务返回0,后台任务返回1
    if(argv[0]==NULL) return;
    if(!builtin_cmd(argv))//非内置指令时执行
    {
        sigprocmask(SIG_BLOCK,&mask,&prev);//屏蔽SIGCHLD
        if((pid=fork())==0)//子进程
        {
            sigprocmask(SIG_SETMASK,&prev,NULL);
            setpgid(0,0);//每个任务单独开一个进程组,方便信号处理中使用kill
            if(execve(argv[0],argv,environ)<0)
            {
                printf("%s:Command not found.\n",argv[0]);
                exit(0);
            }
        }
   
        else//父进程
        {
            if(bg)//后台任务
                addjob(jobs,pid,BG,cmdline);
            else//前台任务
                addjob(jobs,pid,FG,cmdline);
            sigprocmask(SIG_SETMASK,&prev,NULL);
            if(bg)//后台任务不等待任务结束
                printf("[%d](%d)%s",pid2jid(pid),pid,cmdline);
            else//前台任务,等待任务结束
                waitfg(pid);
        }
    }
    return;
}

因为在SIGCHLD的处理函数中,会将已经结束的子进程从任务列表中删除。此处可能会出现竞争引发的错误:当在eval函数中fork后,父进程挂起,子进程执行到进程结束,此时父进程收到SIGCHLD信号,跳转至信号处理函数,将已经完成的子进程从任务列表中删除。注意!!!!此时的父进程还没有执行addjob,即应该被删除的进程根本就没有添加到任务列表中去。所以在子进程执行前需要屏蔽掉SIGCHLD,在父进程完成addjob后再解除屏蔽。

builtin_cmd
int builtin_cmd(char **argv) 
{
    if(!strcmp(argv[0],"quit"))
        exit(0);
    if(!strcmp(argv[0],"jobs"))
    {
        listjobs(jobs);
        return 1;
    }
    if(!strcmp(argv[0],"bg"))
    {   do_bgfg(argv);
        return 1;
    }
    if(!strcmp(argv[0],"fg"))
    {
        do_bgfg(argv);
        return 1;
    }
    return 0;     //非内置指令
}
do_fgbg
void do_bgfg(char **argv) 
{
    int id;
    struct job_t *job;
    //解析指令,指令格式给bg %jid或者 bg pid
    if(argv[1][0]=='%')
    {
        if(argv[1][1]>='0'&&argv[1][1]<='9')
        {
            id=atoi(argv[1]+1);
            job=getjobjid(jobs,id);
            if(job==NULL)
            {
                printf("%s:No such job\n",argv[1]);
                return ;
            }
        }
    }
    else
    {
        if(argv[1][0]>='0'&&argv[1][0]<='9')
        {
            id=atoi(argv[1]);
            job=getjobpid(jobs,id);
            if(job==NULL)
            {
                printf("(%s):No such process\n",argv[1]);
                return ;
            }
        }
    }
    
    kill(-(job->pid),SIGCONT);//让任务继续
    if(!strcmp(argv[0],"bg"))//将任务放至后台,不再等待其结束
    {
        job->state=BG;
        printf("[%d] (%d) %s",job->jid,job->pid,job->cmdline);
    }
    else//将任务放至前台,等待结束
    {
        job->state=FG;
        waitfg(job->pid);
    }
    return;
}
waitfg
void waitfg(pid_t pid)
{
    sigset_t m;
    sigemptyset(&m);
    while(pid==fgpid(jobs))
        //sleep(0);//不使用忙等
        sigsuspend(&m);//有信号时被唤醒检查前台进程pid是否变化,变化则说明前台进程结束。
    return;
}
sigchle_handler
void sigchld_handler(int sig) 
{
    pid_t pid;
    int status;
    while((pid=waitpid(-1,&status,WNOHANG|WUNTRACED))>0)
    {
        if(WIFEXITED(status))//正常结束
        {
            deletejob(jobs,pid);
        }
        if(WIFSTOPPED(status))//任务挂起时
        {
            struct job_t *job=getjobpid(jobs,pid);
            int jid=pid2jid(pid);
            printf("Job [%d] (%d) stopped by signal %d\n",jid,pid,WSTOPSIG(status));
            job->state=ST;
        }
        if(WIFSIGNALED(status))//任务被终止
        {
            int jid=pid2jid(pid);
            printf("Job [%d] (%d) terminated by signal %d\n",jid,pid,WTERMSIG(status));
            deletejob(jobs,pid);
        }

    }
    return;
}
sigint_handler
void sigint_handler(int sig) 
{
    pid_t pid=fgpid(jobs);
    if(pid!=0)
    {
        kill(-pid,sig);
    }
    return;
}

因为每个任务单独一个进程组,直接使用kill向对应进程组发送信号即可。
sigtstp_handler与sigint_handler相同

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值