zoukankan      html  css  js  c++  java
  • APUE学习笔记——10.18 system函数 与waitpid

    system函数

    system函数用方便在一个进程中执行命令行(一行shell命令)。
    用法如下:
    #include <stdio.h>
    #include <stdlib.h>
    
    int main()
    {
        printf("Hello
    ");
        system("sleep 5");
        return 0;
    }
    
    在程序中通过system调用了命令行 sleep 5。(这里知识举一个例子,当然可以执行一个类似“ bash test.sh”之类的脚本
    在这个小程序的运行时,可以通过ps -aux 看到新增加了三个进程。
    一个是我们程序本身a.out
    一个是shell进程:sh -c ***
    一个是我们执行的命令行进程:sleep进程


    system函数的实现:

    《APUE》中给出了system的一中实现:

    #include        <sys/wait.h>
    #include        <errno.h>
    #include        <signal.h>
    #include        <unistd.h>
    
    int
    system(const char *cmdstring)   /* with appropriate signal handling */
    {
            pid_t                           pid;
            int                                     status;
            struct sigaction        ignore, saveintr, savequit;
            sigset_t                        chldmask, savemask;
    
            if (cmdstring == NULL)
                    return(1);              /* always a command processor with UNIX */
    
            ignore.sa_handler = SIG_IGN;    /* ignore SIGINT and SIGQUIT */
            sigemptyset(&ignore.sa_mask);
            ignore.sa_flags = 0;
            if (sigaction(SIGINT, &ignore, &saveintr) < 0)
                    return(-1);
            if (sigaction(SIGQUIT, &ignore, &savequit) < 0)
                    return(-1);
            sigemptyset(&chldmask);                 /* now block SIGCHLD */
            sigaddset(&chldmask, SIGCHLD);
            if (sigprocmask(SIG_BLOCK, &chldmask, &savemask) < 0)
                    return(-1);
    
            if ((pid = fork()) < 0) {
                    status = -1;    /* probably out of processes */
            } else if (pid == 0) {                  /* child */
                    /* restore previous signal actions & reset signal mask */
                    sigaction(SIGINT, &saveintr, NULL);
                    sigaction(SIGQUIT, &savequit, NULL);
                    sigprocmask(SIG_SETMASK, &savemask, NULL);
    
                    execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);
                    _exit(127);             /* exec error */
            } else {                                                /* parent */
                    while (waitpid(pid, &status, 0) < 0)
                            if (errno != EINTR) {
                                    status = -1; /* error other than EINTR from waitpid() */
                                    break;
                            }
            }
    
            /* restore previous signal actions & reset signal mask */
            if (sigaction(SIGINT, &saveintr, NULL) < 0)
                    return(-1);
            if (sigaction(SIGQUIT, &savequit, NULL) < 0)
                    return(-1);
            if (sigprocmask(SIG_SETMASK, &savemask, NULL) < 0)
                    return(-1);
    
            return(status);
    }
    
    很多人对这个实现有不少的疑问。疑问点主要在下面几个方面:
    首先:为什么要忽略中断信号SIGINT和停止信号SIGQUIT。
    其次:为什么要锁住子进程结束信号SIGCHLD
    另外:为什么锁住了子进程结束信号SIGCHLD后,waitpid还能正确返回。


    1. 为什么要忽略中断信号SIGINT和停止信号SIGQUIT。
    前面已经介绍,./a.out执行时,产生了三个进程:a.out、(shell进程)sh -c、system调用的命令行产生的进程(如sleep 5, 在apue中是ed进程)。
    如果不忽略SIGINT和SIGQUIT会出现上面情况呢,《APUE》中通过自己设计的不屏蔽SIGINT和SIGQUIT信号的实现进行了测试,发现SIGINT和SIGQUIT信号会向./a.out产生的三个进程发送,即会发送给a.out、sh -c、ed。(其中sh -c默认忽略此信号),这样会捕获信号的有a.out、ed。这样在实际运行中会出现上面情况呢:在我们要通过Ctl+c关闭ed(这种交互式程序经常会用ctl+c来关闭)时,信号也被发送给了a.out,也就是a.out和ed都被关闭了,这样可能a.out还有其他工作也不能完成了。
    出于以上的原因,我们要求system屏蔽SIGINT和SIGQUIT。

    2.为什么要锁住子进程结束信号SIGCHLD
    首先明白,如果不锁住SIGCHLD,那么在system执行的命令行创建的子进程(如ed)结束时,会向./a.out也发送SIGCHLD信号。这会产生什么问题呢?
    加入我们在调用system之前,./a.out创建了一个子进程(子进程A),并且是通过wait函数等待他结束。但是我们的ed进程结束时就像a.out发送了SIGCHLD,导致wait直接返回,由此a.out就无法等到子进程A结束了。

    3.为什么锁住了子进程结束信号SIGCHLD后,waitpid还能正确返回。
    这一部分我还没找到确切的证据,但描述的也都是一些合理的推断:
    我认为wait和waitpid并不是通过捕获SIGCHLD来返回子进程结束状态的。理由如下:
    a.Linux系统对SIGCHLD的默认处理方式就是ignore
    b.经常看到一些code里,捕获SIGCHLD,然后再SIGCHLD的信号处理函数中调用wait,来使得父进程不阻塞。
    c.waitpid中的option选项为WNOHANG时,waitpid可以立即返回。也就是可以循环调用waitpid来判断子进程是否退出。
    基于以上三点,基本上可以退出waitpid并不是通过捕获SIGCHLD来判断子进程状态的。而应该是通过阻塞读取内核某个状态值









  • 相关阅读:
    linux之 awk
    linux之 sed命令
    HBase源码分析之WAL
    oracle之 单实例监听修改端口
    oracle之 ORA-12557: TNS: 协议适配器不可加载
    oracle之 反向键索引
    oracle之 AWR固定基线
    oracle之 如何 dump logfile
    oracle 之 CLUSTER_INTERCONNECTS is not set to the recommended value
    oracle之 变更OS时间对数据库的影响
  • 原文地址:https://www.cnblogs.com/Windeal/p/4284644.html
Copyright © 2011-2022 走看看