CSAPP--ShellLab

ShellLab

一、 Lab介绍

CSAPP的ShellLab是实现一个自己的shell程序,完成之后可以熟练掌握UNIX关于进程的系统调用(例如fork、waitpid等),熟悉Linux的信号机制,而且这个Lab非常有趣。

二、 Lab实现

实现这个Lab主要是完成以下几个函数的编写:
CSAPP--ShellLab

eval实现

void eval(char *cmdline) 
{
    char *argv[MAXARGS];
    char buf[MAXLINE];
    int bg; // Should the job run in bg or fg
    pid_t pid;
    sigset_t mask_all,mask_one,prev_mask;
    
    strcpy(buf,cmdline);
    
    bg = parseline(buf,argv);
    
    if( argv[0] == NULL ){
        return; // Ignore empty lines
    }
    
    if( !builtin_cmd(argv) ){
        
        sigemptyset(&mask_one);
        sigfillset(&mask_all);
        sigaddset(&mask_one,SIGCHLD);
        
        // 父进程在创建子进程时,不要响应子进程结束中断,故添加SIGCHLD到阻塞队列
        sigprocmask(SIG_BLOCK,&mask_one,&prev_mask);
        if( (pid = fork()) == 0 ){ // Child process
            // 将子进程单独放入一个进程组里面,否则在删除job的时候可能删除shell进程的别的子进程
            setpgid(0, 0);
            // 取消阻塞,这一步非常关键,因为子进程继承了父进程的阻塞队列,故应该将子进程的阻塞队列清空
            sigprocmask(SIG_SETMASK,&prev_mask,NULL);
            if( execve(argv[0],argv,environ) < 0 ){
                printf("%s: Command not found.\n",argv[0]);
                exit(1);
            }
        }
        
        // 给 addjob 加锁,保证原子操作(因为jobs是一个全局变量,而有些中断的处理器也会对jobs进行操作,这样就不安全了)
        sigprocmask(SIG_BLOCK,&mask_all,NULL);
        addjob(jobs,pid,bg ? BG : FG,cmdline);
        sigprocmask(SIG_SETMASK,&prev_mask,NULL);
        
        // Parent process waits for foreground job to terminate
        if( !bg ){
            
            waitfg(pid);
            
        }else {
            
            printf("[%d] (%d) %s",pid2jid(pid),pid,cmdline);
            
        }
    }
    
    return;
}

buildin_cmd实现

int builtin_cmd(char **argv) 
{
    if( !strcmp(argv[0],"quit") ){
        kill(getpid(),SIGQUIT);
        return 1;
    }else if( !strcmp(argv[0],"jobs") ){
        listjobs(jobs);
        return 1;
    }else if( !strcmp(argv[0],"&") ){
        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) 
{
    struct job_t* job;
    char* flag;
    int jid;
    pid_t pid;
    
    flag = argv[1];
    
    if( flag == NULL ){
        printf("%s command requires PID or %%jobid argument",argv[0]);
        return;
    }
    
    // if it is a jid
    if( flag[0] == '%' ){
        jid = atoi(&flag[1]);
        job = getjobjid(jobs,jid);
        if( job == NULL ){
            printf("(%s)No such job\n",flag);
            return;
        }else{
            // get the pid if a valid job for later to kill
            pid = job->pid;
        }
    }else if(isdigit(flag[0])){ // if it is a pid
        pid = atoi(flag);
        // get jid
        jid = pid2jid(pid);
        // get job
        job = getjobjid(jobs,jid);
        if( job == NULL ){
            printf("(%d)No such process\n",pid);
            return;
        }
    }else{
        printf("%s: argument must be a PID or %%jobid\n",argv[0]);
        return;
    }
    
    // kill for each time
    kill(-pid,SIGCONT);
    
    if( !strcmp("fg",argv[0]) ){
        // wait for fg
        job->state = FG;
        waitfg(job->pid);
    }else{
        // print for bg
        printf("[%d] (%d) %s\n",job->jid,job->pid,job->cmdline);
        job->state = BG;
    }
    
}

waitfg实现

void waitfg(pid_t pid)
{
    struct job_t* job = getjobpid(jobs,pid);
    
    if( job == NULL ){
        return;
    }
    
    while( pid == fgpid(jobs) ){ // spin
        sleep(1);
    }
}

sigchld_handler实现

void sigchld_handler(int sig) 
{
    int old_errno = errno;
    
    pid_t pid;
    int status;
    
    while( (pid = waitpid(-1,&status,WNOHANG | WUNTRACED)) > 0 ){ // 因为上面提示信息说明不要等待任何子进程终止,所以这里 options 使用 WNOHANG | WUNTRACED
        // enter here means that one of child has changed status
        if( WIFEXITED(status) ){ // exit normally or call exit(0) or call exit(1)
            // delete terminated child
            deletejob(jobs,pid);
            
        }else if( WIFSIGNALED(status) ){ // terminated by received a signal
            
            int jid = pid2jid(pid);
            
            printf("Job [%d] (%d) terminated by signal %d\n",jid,pid,WTERMSIG(status));
            
            deletejob(jobs,pid);
            
        }else if( WIFSTOPPED(status) ){ // stop
        
            struct job_t* job = getjobpid(jobs,pid);
            
            job->state = ST;
        
            int jid = pid2jid(pid);
            
            printf("Job [%d] (%d) stopped by signal %d\n",jid,pid,WSTOPSIG(status));
            
        }
        
    }
    
    
    errno = old_errno;
    return;
}

sigint_handler实现

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

sigstp_handler实现

void sigtstp_handler(int sig) 
{
    int old_errno = errno;
    pid_t pid = fgpid(jobs);
    
    if( pid == 0 ){
        return;
    }
    
    kill(-pid,sig);
    errno = old_errno;
}
上一篇:go-基础-任务池


下一篇:监控oracle job 运行状况