ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

shlab

2021-11-27 17:00:44  阅读:186  来源: 互联网

标签:sigprocmask mask pid job shlab SIG prev


深入理解计算机系统ch8---shlab

感觉不熟悉异常控制流中章节中的内容时,写起来比较费劲,代码量不大但是要理清楚什么时候该阻塞信号,什么时候该取消阻塞。

# eval函数主要参照书中的方式
void eval(char *cmdline) 
{
    char *argv[MAXARGS];
    char buf[MAXLINE];  //这个buf感觉可以不需要
    int bg;
    pid_t pid;

    sigset_t mask_all, mask_one, prev;
    sigfillset(&mask_all);
    sigemptyset(&mask_one);  
    sigaddset(&mask_one, SIGCHLD);

    strcpy(buf, cmdline);
    bg = parseline(buf, argv);
    if (argv[0] == NULL)
        return;

    if (!builtin_cmd(argv)) {  //如果不是内置命令

        sigprocmask(SIG_BLOCK, &mask_one, &prev);
        if ((pid = fork()) < 0)
            unix_error("fork error");

        if (pid == 0) { /* child process run the jobs */
            sigprocmask(SIG_SETMASK, &prev, NULL);
            setpgid(0, 0);  //这里将每个Job设置成单独一个组,且组ID等于其PID,后面发送信号的时候就比较方便
            if (execve(argv[0], argv, environ) < 0) {
                printf("%s: Command not found.\n", argv[0]);
                exit(0);
            }
        } 

        sigprocmask(SIG_BLOCK, &mask_all, NULL);

        /* father process wait for foreground job to terminate */
        if (!bg) {
            addjob(jobs, pid, FG, buf);
            sigprocmask(SIG_SETMASK, &prev, NULL); //这里解除阻塞,我的想法是防止waitfg函数执行时,因为信号的阻塞导致循环的条件一直成立从而
            /* wait */
            waitfg(pid);
        } else {
            addjob(jobs, pid, BG, buf);
            int jid = pid2jid(pid);
            printf("[%d] (%d) %s", jid, pid, buf);
            sigprocmask(SIG_SETMASK, &prev, NULL);
        }
    }

    return;
}
int builtin_cmd(char **argv) 
{

    sigset_t mask_all, prev;
    sigfillset(&mask_all);
    if (!strcmp(argv[0], "quit")) {
        exit(0);
    }

    if (!strcmp(argv[0], "jobs")) {
        sigprocmask(SIG_BLOCK, &mask_all, &prev);
        listjobs(jobs);  //涉及到全局变量的读取,阻塞信号
        sigprocmask(SIG_SETMASK, &prev, NULL);
        return 1;
    }

    if (!strcmp(argv[0], "fg") || !strcmp(argv[0], "bg")) {
        do_bgfg(argv);
        return 1;
    }

    return 0;     /* not a builtin command */
}
void do_bgfg(char **argv) 
{
    char *jobstr = argv[1];
    if (jobstr == NULL) {
        printf("%s command requires PID or %%jid argument\n", argv[0]);
        return;
    }

    int isjid = 0, jid; //isjid用来判断fg,bg命令之后跟的是%jid还是PID
    pid_t pid;
    if (jobstr[0] == '%') 
        isjid = 1;

    jobstr += isjid;
    if (!(strspn(jobstr, "0123456789") == strlen(jobstr))) {
        printf("%s: argument must be a PID or %%jid\n", jobstr);
        return;
    }

    sigset_t mask, prev;
    sigfillset(&mask);
    sigprocmask(SIG_BLOCK, &mask, &prev);

    if (isjid) {
        jid = atoi(jobstr);
    }
    else {
        pid = atoi(jobstr);
        jid = pid2jid(pid);
    }

    struct job_t *job = getjobjid(jobs, jid);
    if (job == NULL) {
        if (isjid) {
            printf("%%%d: No such Job\n", jid);
            sigprocmask(SIG_SETMASK, &prev, NULL);
            return;
        }
        printf("%d: No such Process\n", pid);
        sigprocmask(SIG_SETMASK, &prev, NULL);
        return;
    }

    if (argv[0][0] == 'b') {
        if (job->state == ST) {
            job->state = BG;
            kill(-(job->pid), SIGCONT);
            printf("[%d] (%d) %s", job->jid, job->pid, job->cmdline);
            sigprocmask(SIG_SETMASK, &prev, NULL);
        }
    }
    
    if (argv[0][0] == 'f') {
        if (job->state == ST) {
            job->state = FG;
            kill(-(job->pid), SIGCONT);
            sigprocmask(SIG_SETMASK, &prev, NULL); //同上
            waitfg(job->pid);
        }
        else if (job->state == BG) {
            job->state = FG;
            sigprocmask(SIG_SETMASK, &prev, NULL); //理由和上面相同
            waitfg(job->pid);
        }
    }

    return;
}
# waitfg的写法,作业给的提示中说是使用忙等待,也就是循环
# 使用sigsuspend的话,需要额外增加一个全局变量
# 参考了其他博客之后,感觉使用循环更好
void waitfg(pid_t pid)
{
    /*
    sigset_t mask;
    sigemptyset(&mask);
    fg_pid = 0;
    while (!fg_pid)
        sigsuspend(&mask);

    */
    sigset_t mask, prev;
    sigfillset(&mask);
    sigprocmask(SIG_BLOCK, &mask, &prev);
    struct job_t *job = getjobpid(jobs, pid);  //每次要从全局变量jobs中读取内容是都要阻塞信号
    sigprocmask(SIG_SETMASK, &prev, NULL);
    while (job != NULL && job->state == FG) {  //判断pid对应的job是否为空,为空则不用再等待,如果工作非空,但是其状态不再为FG时,也退出等待
        sigfillset(&mask);
        sigprocmask(SIG_BLOCK, &mask, &prev);
        job = getjobpid(jobs, pid);
        sigprocmask(SIG_SETMASK, &prev, NULL);
    }

    return;
}
# 关键在于识别好发送SIGCHLD信号的原因,也就是进程是停止(stopped),终止(terminated)还是自然退出的(exited)
# waitpid(-1, &status, WNOHANG | WUNTRACED)如果子进程中有任何的进程停止,终止或者自然退出,都会立即返回。然后根据state来判断返回的pid对应的进程是因为什么原因导致SIGCHLD信号产生的
void sigchld_handler(int sig) 
{
    int olderrno = errno;    
    pid_t pid;
    int status;

    while (((pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0)) {
        sigset_t mask_all, prev;
        sigfillset(&mask_all);
        sigprocmask(SIG_BLOCK, &mask_all, &prev);
        if (WIFSIGNALED(status)) {  //因为ctrl + c导致的进程停止
            pid_t curr_fg_pid = fgpid(jobs);
            if (pid == curr_fg_pid) {
                int jid = pid2jid(pid);
                printf("Job [%d] (%d) Terminated by signal %d\n", jid, pid, WTERMSIG(status));
                deletejob(jobs, pid);
            }
        } 
        else if (WIFEXITED(status)) {   /* 处理正常终止的进程 */
            deletejob(jobs, pid);
        }
        else if (WIFSTOPPED(status)) {  /* ctrl + z导致的进程停止 */
            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));
        }
        sigprocmask(SIG_SETMASK, &prev, NULL);
    }
    errno = olderrno;

    return;
}
void sigint_handler(int sig) 
{   /* remove FG job and terminate the process */
    int olderrno = errno;
    sigset_t mask_all, prev;
    sigfillset(&mask_all);
    sigprocmask(SIG_BLOCK, &mask_all, &prev);
    pid_t pid = fgpid(jobs);
    if (pid) {  //判断一下是不是前台进程,不是的话就不生效
        kill(-pid, sig); //这里是-pid,与之前的将每个工作单独放到一个进程组对应起来
    }
    sigprocmask(SIG_SETMASK, &prev, NULL);
    errno = olderrno;

    return;
}

# sigtstp_hadler类似
void sigtstp_handler(int sig) 
{
    int olderrno = errno;
    sigset_t mask_all, prev;
    sigprocmask(SIG_BLOCK, &mask_all, &prev);

    pid_t pid = fgpid(jobs);
    if (pid) {
        kill(-pid, sig);
    }
    sigprocmask(SIG_SETMASK, &prev, NULL);
    errno = olderrno;

    return;
}

参考博客链接
https://www.cnblogs.com/jjppp/p/14686584.html
https://blog.csdn.net/xiaolian_hust/article/details/80087376

标签:sigprocmask,mask,pid,job,shlab,SIG,prev
来源: https://www.cnblogs.com/mujueke/p/15612329.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有