《深入理解計算機系統(tǒng)/CSAPP》Shell Lab

原文鏈接

目標

補全tsh.c中剩余的代碼:

  • void eval(char *cmdline):解析并執(zhí)行命令。
  • int builtin_cmd(char **argv):檢測命令是否為內(nèi)置命令quit向臀、fg宵膨、bg架谎、jobs
  • void do_bgfg(char **argv):實現(xiàn)bg辟躏、fg命令谷扣。
  • void waitfg(pid_t pid):等待前臺命令執(zhí)行完成。
  • 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執(zhí)行第n組測試數(shù)據(jù)的輸出拨黔。

使用make rtestn用來測試參考shell程序第n組測試數(shù)據(jù)的輸出(共16組測試數(shù)據(jù))。

tshref.out包含參考shell程序的所有測試數(shù)據(jù)的輸出結(jié)果绰沥,先看完該文件了解命令格式在開始編碼篱蝇。

說明

可用輔助函數(shù):

  • int parseline(const char *cmdline,char **argv):獲取參數(shù)列表char **argv,返回是否為后臺運行命令(true)徽曲。
  • void clearjob(struct job_t *job):清除job結(jié)構(gòu)零截。
  • 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號轉(zhuǎn)化為jid
  • void listjobs(struct job_t *jobs):打印jobs稚虎。
  • void sigquit_handler(int sig):處理SIGQUIT信號撤嫩。

eval

  • 為避免子進程在未加入到jobs鏈表中就發(fā)送信號(SIGINTSIGCHLD蠢终、SIGTSTP)去處理jobs鏈表(競爭)序攘,在子進程創(chuàng)建前需要將SIGCHLD阻塞茴她,在加入jobs鏈表后解鎖該阻塞。
  • fork后的子進程會繼承父進程的信號屏蔽字并且在exec后仍會繼承程奠,所以需要在執(zhí)行可執(zhí)行文件前復原信號屏蔽字丈牢。
  • 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

  • 使用循環(huán)檢查泛粹,簡單實現(xiàn)。
  • 參考《深入理解計算機第三版》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

  • 最后兩個函數(shù)们衙,均向子進程發(fā)送信號。
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;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末碱呼,一起剝皮案震驚了整個濱河市蒙挑,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌愚臀,老刑警劉巖忆蚀,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異姑裂,居然都是意外死亡馋袜,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進店門舶斧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來欣鳖,“玉大人,你說我怎么就攤上這事茴厉≡筇ǎ” “怎么了?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵矾缓,是天一觀的道長怀酷。 經(jīng)常有香客問我,道長而账,這世上最難降的妖魔是什么胰坟? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上笔横,老公的妹妹穿的比我還像新娘竞滓。我一直安慰自己,他們只是感情好吹缔,可當我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布商佑。 她就那樣靜靜地躺著,像睡著了一般厢塘。 火紅的嫁衣襯著肌膚如雪茶没。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天晚碾,我揣著相機與錄音抓半,去河邊找鬼。 笑死格嘁,一個胖子當著我的面吹牛笛求,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播糕簿,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼探入,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了懂诗?” 一聲冷哼從身側(cè)響起蜂嗽,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎殃恒,沒想到半個月后植旧,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡芋类,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年隆嗅,在試婚紗的時候發(fā)現(xiàn)自己被綠了界阁。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片侯繁。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖泡躯,靈堂內(nèi)的尸體忽然破棺而出贮竟,到底是詐尸還是另有隱情,我是刑警寧澤较剃,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布咕别,位于F島的核電站,受9級特大地震影響写穴,放射性物質(zhì)發(fā)生泄漏惰拱。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一啊送、第九天 我趴在偏房一處隱蔽的房頂上張望偿短。 院中可真熱鬧欣孤,春花似錦、人聲如沸昔逗。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽勾怒。三九已至婆排,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間笔链,已是汗流浹背段只。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留鉴扫,地道東北人翼悴。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像幔妨,于是被迫代替她去往敵國和親鹦赎。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,925評論 2 344