zoukankan      html  css  js  c++  java
  • linux系统编程之信号(四)

    今天继续探讨信号相关的东东,话不多说,正入正题:

    信号在内核中的表示:
    下面用图来进一步描述这种信号从产生到递达之间的状态(信号阻塞与未诀):
     
    那是怎么来决定的呢?下面慢慢来举例分解:
    所以,通过这些图,可以描述信号从产生到递达的一个过程,上面的理解起来可能有点难,下面会用代码来进一步阐述,在进行实验之前,还需了解一些函数的使用,这些函数在实验中都会被用到,也就是信号集操作函数。
    信号集操作函数:
    其中解释一下sigset_t,百度百科解释为:
    而这个函数的意义就是将这64位清0
    这个函数的意义是将这屏蔽字的64位都变为1
    将这个信号所对应的位置为1
    将这个信号所对应的位置为0
    检测这一个信号所对应的位当前是0还是1
     
    以上是操作信号集的五个相关的函数,但是注意:这五个函数仅仅是改变这个信号集变量,如set,并非真正改变进程信号当中的屏蔽字,所以接下来介绍的函数就是改变信号当中的屏蔽字的
    sigprocmask:
    好了,理论说了很多,下面正式开始实验,来体会一下一个信号从产生到递达的一个状态转换过程:
    首先,我们打印出系统的未诀信号,目的是为了观察之后的状态:
    #include <unistd.h>
    #include <sys/stat.h>
    #include <sys/wait.h>
    #include <sys/types.h>
    #include <fcntl.h>
    
    #include <stdlib.h>
    #include <stdio.h>
    #include <errno.h>
    #include <string.h>
    #include <signal.h>
    
    
    #define ERR_EXIT(m) 
        do 
        { 
            perror(m); 
            exit(EXIT_FAILURE); 
        } while(0)
    
    void handler(int sig);
    void printsigset(sigset_t *set)//打印出信号集的状态,其中参数set为未诀状态的信号集
    {
        int i;
        for (i=1; i<NSIG; ++i)//NSIG表示信号的最大值,也就是等于64
        {
            if (sigismember(set, i))//说明是未诀状态的信号
                putchar('1');
            else
                putchar('0');//说明不是未诀状态的信号
        }
        printf("
    ");
    }
    
    int main(int argc, char *argv[])
    {
        sigset_t pset;
    
        for (;;)
        {
            sigpending(&pset);//该函数是获取进程当中未诀状态的信号集 ,保存在pset当中
            printsigset(&pset);//打印信号集的状态,看有没有未诀状态的信号产生
            sleep(1);
        }
        return 0;
    }

    【说明】:sigpending是用来获取进程中所有的未诀信号集:

    这时看一下运行效果:

    可以发现,当前状态没有未诀的信号,因为还没有被阻塞的信号过,信号也没有产生过,所以不可能有未诀的状态。
    这时,我们来安装一个SIGINT信号:

    #include <unistd.h>
    #include <sys/stat.h>
    #include <sys/wait.h>
    #include <sys/types.h>
    #include <fcntl.h>
    
    #include <stdlib.h>
    #include <stdio.h>
    #include <errno.h>
    #include <string.h>
    #include <signal.h>
    
    
    #define ERR_EXIT(m) 
        do 
        { 
            perror(m); 
            exit(EXIT_FAILURE); 
        } while(0)
    
    void handler(int sig);
    void printsigset(sigset_t *set)
    {
        int i;
        for (i=1; i<NSIG; ++i)
        {
            if (sigismember(set, i))
                putchar('1');
            else
                putchar('0');
        }
        printf("
    ");
    }
    
    int main(int argc, char *argv[])
    {
        sigset_t pset;
        if (signal(SIGINT, handler) == SIG_ERR)//安装一个SIGINT信号
            ERR_EXIT("signal error");
    
        for (;;)
        {
            sigpending(&pset);
            printsigset(&pset);
            sleep(1);
        }
        return 0;
    }
    
    void handler(int sig)
    {
            printf("recv a sig=%d
    ", sig);
    }

    这时再看下效果:

    从结果来看,信号被直接递达了,所以这次也没有看到有1的未诀状态的信号,因为信号必须被阻塞才会出现未诀状态,所以接下来将SIGINT信号利用上面介绍到的函数来将其阻塞掉:

    #include <unistd.h>
    #include <sys/stat.h>
    #include <sys/wait.h>
    #include <sys/types.h>
    #include <fcntl.h>
    
    #include <stdlib.h>
    #include <stdio.h>
    #include <errno.h>
    #include <string.h>
    #include <signal.h>
    
    
    #define ERR_EXIT(m) 
        do 
        { 
            perror(m); 
            exit(EXIT_FAILURE); 
        } while(0)
    
    void handler(int sig);
    void printsigset(sigset_t *set)
    {
        int i;
        for (i=1; i<NSIG; ++i)
        {
            if (sigismember(set, i))
                putchar('1');
            else
                putchar('0');
        }
        printf("
    ");
    }
    
    int main(int argc, char *argv[])
    {
        sigset_t pset;
        
        sigset_t bset;
        sigemptyset(&bset);//将信号集清0
        sigaddset(&bset, SIGINT);//将SIGINT所对应的位置1
    
        if (signal(SIGINT, handler) == SIG_ERR)
            ERR_EXIT("signal error");
    
        sigprocmask(SIG_BLOCK, &bset, NULL);//更改进程中的信号屏蔽字,其中第三个参数传NULL,因为不关心它原来的信号屏蔽字
        for (;;)
        {
            sigpending(&pset);
            printsigset(&pset);
            sleep(1);
        }
        return 0;
    }
    
    void handler(int sig)
    {
            printf("recv a sig=%d
    ", sig);
    }

    编译运行:

    从结果来看,将SIGINT信号来了,由于添加到了信号屏蔽字为1,所以会被阻塞掉,并且可以看到SIGINT对应的位也打印为1了。

    【说明】:SIGINT对应的位是指:

    下面,我们做一件事情,就是当我们按下ctrl+解除阻塞,这样处于未诀状态的信号就会被递达,则对应的未诀状态位也会还原成0,具体代码如下:

    #include <unistd.h>
    #include <sys/stat.h>
    #include <sys/wait.h>
    #include <sys/types.h>
    #include <fcntl.h>
    
    #include <stdlib.h>
    #include <stdio.h>
    #include <errno.h>
    #include <string.h>
    #include <signal.h>
    
    
    #define ERR_EXIT(m) 
        do 
        { 
            perror(m); 
            exit(EXIT_FAILURE); 
        } while(0)
    
    void handler(int sig);
    void printsigset(sigset_t *set)
    {
        int i;
        for (i=1; i<NSIG; ++i)
        {
            if (sigismember(set, i))
                putchar('1');
            else
                putchar('0');
        }
        printf("
    ");
    }
    
    int main(int argc, char *argv[])
    {
        sigset_t pset;
        sigset_t bset;
        sigemptyset(&bset);
        sigaddset(&bset, SIGINT);
        if (signal(SIGINT, handler) == SIG_ERR)
            ERR_EXIT("signal error");
        if (signal(SIGQUIT, handler) == SIG_ERR)//注册一个ctrl+c信号
            ERR_EXIT("signal error");
    
        sigprocmask(SIG_BLOCK, &bset, NULL);
        for (;;)
        {
            sigpending(&pset);
            printsigset(&pset);
            sleep(1);
        }
        return 0;
    }
    
    void handler(int sig)
    {
        if (sig == SIGINT)
            printf("recv a sig=%d
    ", sig);
        else if (sig == SIGQUIT)
        {
            sigset_t uset;//当按下ctrl+时,则对SIGINT信号解除阻塞
            sigemptyset(&uset);
            sigaddset(&uset, SIGINT);
            sigprocmask(SIG_UNBLOCK, &uset, NULL);
        }
    }

    编译运行:

    从中可以看到,当我们按下ctrl+时,并没有退出,而是解除了阻塞,所以对应的SIGINT位也变为0了。

    另外,看下这种情况:

    多次按了ctrl+c,可在按ctrl+解除阻塞时,只响应了一次信号处理函数,这也由于SIGINT是不可靠信号,不支持排队。

    另外,由于我们捕获了ctrl+信号,所以没办法退出这个进程了,那怎么办呢,可以利用shell命令将其强制杀掉如下:

    好了,今天学的东西可能有些生涩,需好好消化,下节再见!

     
  • 相关阅读:
    02 Hibernate错题分析
    08章 分组查询、子查询、原生SQL
    NHibernate的基本使用
    NHibernate与IbatisNet的简单比较
    NHibernate从入门到精通系列(3)——第一个NHibernate应用程序
    NHibernate从入门到精通系列(2)——NHibernate环境与结构体系
    NHibernate从入门到精通系列(1)——NHibernate概括
    SQL字符串操作汇总
    SVN (TortioseSVN) 版本控制之忽略路径(如bin、obj、gen)
    SVN服务器搭建和使用(三)
  • 原文地址:https://www.cnblogs.com/webor2006/p/3751210.html
Copyright © 2011-2022 走看看