• 信号之system函数


    http://www.cnblogs.com/nufangrensheng/p/3512291.html中已经有了一个system函数的实现,但是该版本并不执行任何信号处理。POSIX.1要求system忽略SIGINT和SIGQUIT,阻塞SIGCHLD。

    实例

    程序清单10-19使用http://www.cnblogs.com/nufangrensheng/p/3512291.html中的system版本,用其调用ed(1)编辑器。(ed很久以来就是UNIX的组成部分。在这里调用它的原因是:它是捕捉中断和退出信号的交互式程序。若 从shell调用ed,并键入中断字符,则它捕捉中断信号并打印问号。它还将对退出符的处理方式设置为忽略。

    程序清单10-19 用system调用ed编辑器

    #include "apue.h"
    
    static void
    sig_int(int signo)
    {
        printf("caught SIGINT
    ");
    }
    
    static void
    sig_chld(int signo)
    {
        printf("caught SIGCHLD
    ");
    }
    
    int
    main(void)
    {
        if(signal(SIGINT, sig_int) == SIG_ERR)
            err_sys("signal(SIGINT) error");
        if(signal(SIGCHLD, sig_chld) == SIG_ERR)
            err_sys("signal(SIGCHLD) error");
    
        if(system("/bin/ed") < 0)
            err_sys("system() error");
        exit(0);
    }

    程序清单10-19用于捕捉SIGINT和SIGCHLD信号。若调用它则可得:

    未命名 

    当编辑器终止时,系统向父进程(a.out进程)发送SIGCHLD信号。父进程捕捉它,然后从信号处理程序返回。但是若父进程正在捕捉SIGCHLD信号(因为它创建了子进程,所以应当这样做以便了解它的子进程在何时终止),那么正在执行system函数时,应当阻塞对父进程递送SIGCHLD信号。实际上,这就是POSIX.1所说明的。否则,当system创建的子进程结束时,system的调用者可能错误地认为,它自己的一个子进程结束了。于是,调用者将会调用一种wait函数以获得子进程的终止状态,这样就阻止了system函数获得子进程的终止状态,并将其作为它的返回值。

    如果再次执行该程序,在这次运行时将一个中断信号传送给编辑器,则可得:

    未命名

    键入中断字符可使中断信号传送给前台进程组中的所有进程。编辑程序正在运行时的各个进程的关系:

      登录shell ---fork/exec---> a.out ---fork/exec---> /bin/sh ---fork/exec---> /bin/ed

      后台进程组                                                        前台进程组

    a.out ---fork/exec---> /bin/sh 是由a.out中调用system函数引起的:http://www.cnblogs.com/nufangrensheng/p/3512291.html

    /bin/sh ---fork/exec---> /bin/ed 可参考类shell程序的简化实现程序清单1-5:http://www.cnblogs.com/nufangrensheng/p/3495129.html

    在这一实例中,SIGINT被送给三个前台进程(shell进程忽略此信号)。从输出中可见a.out进程和ed进程捕捉该信号。但是,当用system运行另一程序(例如ed)时,不应使父子进程两者都捕捉中断产生的两个信号:中断和退出。这两个信号只应送给正在运行的程序:子进程。因为由system执行的命令可能是交互式命令(例如本例中的ed程序),以及因为system的调用者在程序执行时放弃了控制,等待该执行程序的结束,所以system的调用者就不应接收这两个终端产生的信号。这就是为什么POSIX.1规定system的调用者应当忽略这两个信号的原因。(糊里糊涂的感觉)

    实例

    程序清单10-20 system函数的POSIX.1正确实现

    #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)
        {
            /* restore previous signal actions & reset signal mask */
            sigaction(SIGINT, &saveintr, NULL);
            sigaction(SIGQUIT, &savequit, NULL);
            sigprocmask(SIG_SETMASK, &savemask, NULL);         /* 在父进程中忽略SIGINT,SIGQUIT,阻塞SIGCHLD,在子进程中恢复 */
    
            execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);
            _exit(127);    /* exec error */ 
        }
        else
        {
            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);                                           /* 子进程退出后,父进程才恢复SIGINT,SIGQUIT和SIGCHLD */
    
        return(status);
    }

    如果链接程序清单10-19与system函数的这一实现,那么所产生的二进制代码与上一个有缺陷的程序相比较,存在如下差别:

    (1)当我们键入中断或退出字符时,不向调用者进程发送信号。

    (2)当ed命令终止时,不向调用进程发送SIGCHLD信号。作为替代,在程序末尾的sigprocmask调用对SIGCHLD信号解除阻塞之前,SIGCHLD信号一直被阻塞。而对sigprocmask函数的这一次调用是在system函数调用waitpid取到子进程的终止状态之后。

     

    system的返回值

    注意system的返回值,它是shell的终止状态,但shell的终止状态并不总是执行命令字符串进程的终止状态。

    Bourne shell有一个在其文档中没有说清楚的特性:当用一个信号终止了正在执行的命令时,其终止状态是128加上一个信号编号

    用交互方式使用shell可以看到这一点:

    未命名

    在所使用的系统中,SIGINT的值为2,SIGQUIT的值为3,于是给出shell终止状态130、131.

    仅当shell本身异常终止时,system的返回值才报告一个异常终止。

    在编写使用system函数的程序时,一定要正确地解释返回值。如果直接调用fork、exec和wait,则终止状态与调用system是不同的

     

    本篇博文内容摘自《UNIX环境高级编程》(第二版),仅作个人学习记录所用。关于本书可参考:http://www.apuebook.com/

  • 相关阅读:
    疑问
    Android之Adapter用法总结-(转)
    Android之Adapter用法总结(转)
    ListView中每个item条目在被单击选中时能够高亮显示
    使用简单的python语句编写爬虫 定时拿取信息并存入txt
    Python实现简单的爬虫获取某刀网的更新数据
    scrapy-redis(七):部署scrapy
    Scrapy-redis改造scrapy实现分布式多进程爬取
    Scrapy-redis实现分布式爬取的过程与原理
    https://blog.csdn.net/u012150179/article/details/38091411
  • 原文地址:https://www.cnblogs.com/nufangrensheng/p/3517127.html
Copyright © 2020-2023  润新知