MSH:一个简单SH工具实现

本文详述了如何逐步构建一个名为MSH的Shell工具,包括命令求值、前台阻塞处理、回收僵死进程、内嵌功能实现、支持前台进程的终止与暂停、BG/FG功能、功能实现、修改和增加默认目录等。作者分享了在实现过程中遇到的问题及解决方案,如使用`waitpid`替代`wait`,优雅地处理前台阻塞,以及添加`cd`命令等功能。
摘要由CSDN通过智能技术生成

这里写图片描述
本文将分为不同的Part,分别实现Shell的一部分功能。
mshCSAPPSHLAB出发,逐渐完善SHELL功能,并移植到自己的OS上。
Github: https://github.com/He11oLiu/msh

Part1

Part1 目标

  • 首先,tsh需要支持内嵌指令功能,使用int builtin_cmd(char **argv)实现。
  • 再,tsh需要支持前后台执行程序的功能,shell需要接收SIGCHLD进程,回收僵死进程或处理暂停进程。

    在给出的handout中已经把语义分析写好了,直接用即可。

命令求值函数

/*
 * eval - Evaluate the command line that the user has just typed in
 *
 * If the user has requested a built-in command (quit, jobs, bg or fg)
 * then execute it immediately. Otherwise, fork a child process and
 * run the job in the context of the child. If the job is running in
 * the foreground, wait for it to terminate and then return.  Note:
 * each child process must have a unique process group ID so that our
 * background children don't receive SIGINT (SIGTSTP) from the kernel
 * when we type ctrl-c (ctrl-z) at the keyboard.
 */
void eval(char *cmdline) {
    char *argv[MAXARGS];
    int bg;
    pid_t pid;
    sigset_t mask;


    bg = parseline(cmdline,argv);
    if(argv[0] == NULL)
        return ;
    if(!builtin_cmd(argv)){
        sigemptyset(&mask);
        sigaddset(&mask,SIGCHLD);
        sigaddset(&mask,SIGINT);
        sigaddset(&mask,SIGTSTP);
        sigprocmask(SIG_BLOCK,&mask,NULL);
        /* child proc */
        if((pid = Fork()) == 0){
            setpgid(0, 0);
            if(verbose){
                pid = getpid();
                printf("Child proc started with pid %d\n",(int)pid);
            }
            sigprocmask(SIG_UNBLOCK,&mask,NULL);
            if(execve(argv[0],argv,environ)<0){
                printf("%s: Command not found.\n",argv[0]);
                exit(0);
            }
        }
        /* parent proc */
        else{
            addjob(jobs,pid,bg?BG:FG,cmdline);
            sigprocmask(SIG_UNBLOCK,&mask,NULL);
            if(!bg){
                /* Use waitfg to wait until proc(pid) is no longer a frontgroud proc. */
                waitfg(pid);
            }
            else{
                printf("[%d] (%d) %s",pid2jid(pid),pid,cmdline);
            }
        }
    }
    return ;
}

在第一阶段的目标下,不需要在课本的基础上改多少内容,一个是对于前台的子进程不再使用waitpid来阻塞,而是使用更加优雅的waitfg来阻塞。再一个是添加了SIGCHLD的处理函数,用于处理回收僵死或暂停进程。

前台阻塞处理

对于前台,最初实现是直接waitpid。但是一旦使用SIGCHLD的处理函数来回收,两个waitpid会导致结构不好,全局FLAG之类也不够优雅。

发现其提供了waitfg的接口,思路一用以下方法实现。

void waitfg(pid_t pid){
    struct job_t *cur = getjobpid(jobs,pid);
    while(cur != NULL && cur->state == FG){
        cur = getjobpid(jobs,pid);
    }
    /* 2 cases:
     BG      : switch to BG from FG
     NULL    : delete from jobs
     */
    return;
}

性能不佳,且不够优雅。每次都观察前台的proc是不是pid即可,再利用pause暂停,直到有下一个信号来临。

void waitfg(pid_t pid){
    while(pid == fgpid(jobs))
        pause();
    return;
}

这样就优雅多了。

回收僵死进程

需要使用sigchld_handler来接收SIGCHLD信号对僵死进程进行回收或者处理被暂停的进程。

/*
 * sigchld_handler - The kernel sends a SIGCHLD to the shell whenever
 *     a child job terminates (becomes a zombie), or stops because it
 *     received a SIGSTOP or SIGTSTP signal. The handler reaps all
 *     available zombie children, but doesn
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值