csapp lab7 shell-lab记录

话不多说,直接上代码(顺序从易到难,和原题顺序不一致)

1.sigint_handler

一句话概括实现功能:实现一个 SIGINT 信号处理函数,将信号传送给前台进程。即判断如果还有前台任务,则用kill向其发送一个SIGINT信号终止该进程

void sigint_handler(int sig) 
{
    int olderrno = errno;
    pid_t pid;
    sigset_t mask_all, prev;
    sigfillset(&mask_all);
    sigprovmask(SIG_BLOCK, &mask_all, &prev);
    
    if ((pid = fgpid(jobs)) != 0)
    {
        sigprocmask(SIG_SETMASK, &prev, NULL);
        kill(-pid, SIGINT);//注意是给进程组发送信号,因为可能会产生子程序
    }
    errno = olderrno;
    return;
}

PS:注意保存和恢复 errno。很多函数会在出错返回式设置 errno,在处理程序中调用这样的函数可能会干扰主程序中其他依赖于 errno 的部分,解决办法是在进入处理函数时用局部变量保存它,运行完成后再将其恢复

2.sigtstp_handler

一句话概括实现功能:和上题思路一致

void sigint_handler(int sig) 
{
    int olderrno = errno;
    pid_t pid;
    sigset_t mask_all, prev;
    sigfillset(&mask_all);
    sigprovmask(SIG_BLOCK, &mask_all, &prev);
    
    if ((pid = fgpid(jobs)) != 0)
    {
        sigprocmask(SIG_SETMASK, &prev, NULL);
        kill(-pid, SIGINT);//注意是给进程组发送信号,因为可能会产生子程序
    }
    errno = olderrno;
    return;
}

3.waitfg

一句话概括实现功能:等待前台作业完成

void waitfg(pid_t pid)
{
    
	sigset_t mask;
 	sigempty(&mask);
   while(fgpid(jobs)!=0)
   {
   		sigsuspend(&mask);
   }
    return;

}

PS:不用“pause”用“sigsuspend”函数的原因是考虑了竞争的情况,假定父进程在进入循环后,而执行pause前,子进程终止,由于pause仅在捕捉到信号后返回,而之后不会再有任何信号抵达,那么父进程就会永远休眠。

而"sigsuspend"相当于如下代码,1,2行代码同时执行

sigprocmask(SIG_SETMASK, &mask, &prev);
pause();
sigprocmask(SIG_SETMASK, &prev, NULL);

5.builtin_cmd

一句话概括功能:判断是否为内置命令,是的话立即执行并返回1,不是的话返回0

	if(!strcmp(argv[0],"quit"))
	{
		exti(0);
	}
	if((!strcmp(argv[0],"fg"))||(!strcmp(argv[0],"fg")))
	{
		do_bgfg(argv);
		return 1;
	}
	if(!strcmp(argv[0],"jobs"))
	{
		listjobs(jobs);
		return 1;
	}
	if(!strcmp(argv[0],"&"))
	{
		return 1;
	}
	
   
    return 0; 

6.do_fgbg

一句话概括功能:实现fg和bg函数

void do_bgfg(char **argv) 
{
    struct job_t *job = NULL;        //要处理的job
    int state;                      //输入的命令
    int id;                         //存储jid或pid
    if(!strcmp(argv[0], "bg")) state = BG;
        else state = FG;  
    if(argv[1]==NULL){               //没带参数
        printf("%s command requires PID or %%jobid argument\n", argv[0]);
        return;
    }
    if(argv[1][0]=='%'){             //说明是jid
       if(sscanf(&argv[1][1], "%d", &id) > 0){
            job = getjobjid(jobs, id);  //获得job
            if(job==NULL){
                printf("%%%d: No such job\n", id);
                return;
            }
        }
    }
    else if(!isdigit(argv[1][0])) {  //其它符号,非法输入
        printf("%s: argument must be a PID or %%jobid\n", argv[0]);
        return;
    }
    else{                       //pid
        id = atoi(argv[1]);
        job = getjobpid(jobs, id);
        if(job==NULL){
            printf("(%d): No such process\n", id);
            return;
        }

    }
    Kill(-(job->pid), SIGCONT);       //重启进程, 这里发送到进程组
    job->state = state;
    if(state==BG)
        printf("[%d] (%d) %s",job->jid, job->pid, job->cmdline);
    else 
        waitfg(job->pid);
    return;
}

7.eval

一句话概括功能:解析命令是内置命令还是其他命令,是前台作业还是后台作业

void eval(char *cmdline) 
{
    char *argv[MAXARGS];        //存放解析的参数
    char buf[MAXLINE];          //解析cmdline
    int bg;                     //判断程序是前台还是后台执行
    int state;                  //指示前台还是后台运行状态
    pid_t pid;                  //执行程序的子进程的pid

    strcpy(buf, cmdline);   
    bg = parseline(buf, argv);  //解析参数
    state = bg? BG:FG;          
    if(argv[0] == NULL)         //空行,直接返回
        return;
    sigset_t mask_all, mask_one, prev_one;
    Sigfillset(&mask_all);
    Sigemptyset(&mask_one);
    Sigaddset(&mask_one, SIGCHLD);
    if(!builtin_cmd(argv)) {                            //判断是否为内置命令
        Sigprocmask(SIG_BLOCK, &mask_one, &prev_one);       //fork前阻塞SIGCHLD信号
        if((pid = Fork()) == 0) {                           //创建子进程
            Sigprocmask(SIG_SETMASK, &prev_one, NULL);      //解除子进程的阻塞
            Setpgid(0, 0);                                  //创建新进程组,ID设置为进程PID
            Execve(argv[0], argv, environ);                 //执行
            exit(0);                                        //子线程执行完毕后一定要退出
        }
        if(state==FG){
            Sigprocmask(SIG_BLOCK, &mask_all, NULL);            //添加工作前阻塞所有信号
            addjob(jobs, pid, state, cmdline);                  //添加至作业列表
            Sigprocmask(SIG_SETMASK, &mask_one, NULL);
            waitfg(pid);                                        //等待前台进程执行完毕
        }         
        else{
            Sigprocmask(SIG_BLOCK, &mask_all, NULL);            //添加工作前阻塞所有信号
            addjob(jobs, pid, state, cmdline);                  //添加至作业列表
            Sigprocmask(SIG_SETMASK, &mask_one, NULL);                                 
            printf("[%d] (%d) %s",pid2jid(pid), pid, cmdline);  //打印后台进程信息
        }
        Sigprocmask(SIG_SETMASK, &prev_one, NULL);          //解除阻塞 
    }
    return;
}

主要借鉴了知乎大佬“潜龙勿用”的思路

附上链接

CSAPP | Lab7-Shell Lab 深入解析 - 知乎 (zhihu.com)

  • 10
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值