《深入理解计算机系统/CSAPP》Shell Lab

原文链接

目标

补全tsh.c中剩余的代码:

  • void eval(char *cmdline):解析并执行命令。
  • int builtin_cmd(char **argv):检测命令是否为内置命令quitfgbgjobs
  • void do_bgfg(char **argv):实现bgfg命令。
  • 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链表中删除pidjob
  • pid_t fgpid(struct job_t *jobs):返回当前前台运行jobpid号。
  • 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链表中就发送信号(SIGINTSIGCHLDSIGTSTP)去处理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;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 156,265评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,274评论 1 288
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,087评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,479评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,782评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,218评论 1 207
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,594评论 2 309
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,316评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,955评论 1 237
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,274评论 2 240
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,803评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,177评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,732评论 3 229
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,953评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,687评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,263评论 2 267
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,189评论 2 258