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

    今天继续对信号进行研究,话不多说,言归正传:

    更多信号发送函数:
    上节中我们已经接触到了一些信号的发送函数,这里更进一步学习一下其它的发送函数:
    alarm:只能发送SIGALRM信号
    下面通过一个例子来介绍这个函数:
    #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);
    int main(int argc, char *argv[])
    {
        if (signal(SIGALRM, handler) == SIG_ERR)//注册了一个alrm信号
            ERR_EXIT("signal error");
    
        alarm(1);//发送一个alarm信号
        for (;;)
            pause();
        return 0;
    }
    
    void handler(int sig)
    {
        printf("recv a sig=%d
    ", sig);
    }

    关于alarm函数的说明可以查看man:

    运行效果:

    可见,是隔了一秒才发送出alarm信号的,实际上,我们可以找到该进程,用shell命令中人为的发送该信号:

    通过kill命令来发送信号,为了看到效果,新开一个命令终端,效果如下:

    可以看到手动发送信号也是可以正常收到的,另外,我们在发送信号时,既可以用数字,也可以用它对应的名称,如下:

    实际上,对于进程的ID,可以通过动态方式来获取,按如下步骤(了解一下)

    1、先过滤掉其它行

    2、然后再只得到进程ID列,过滤掉其它列

    所以,手动发送信号时,就可以用动态的方式来发送了,如下:
    说明:关于这个语法,在基础linux上会记录到,先了解下这种用法。
    我们发现,alarm函数不能每隔一秒发送一次信号,那如果要做到这点该怎么办呢?
    效果如下:
     
     
     
    setitimer:发送SIGALRM、SIGVTALRM、SIGPROF信号
     
    abort:只能发送SIGABRT信号
     
    可重入函数与不可重入函数:
    关于什么是可重入函数和不可重入函数, 这个比较难以理解,下面先阐述下概念,然后再通过实验来进一步理解:
    太抽象了,下面用实验来说明:
    #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)
    
    
    typedef struct
    {
        int a;
        int b;
    } TEST;
    
    TEST g_data;//定义了一个全局变量,是为了说明不可重入函数的问题
    
    void handler(int sig);
    int main(int argc, char *argv[])
    {
        TEST zeros = {0, 0};
        TEST ones = {1, 1};
        if (signal(SIGALRM, handler) == SIG_ERR)
            ERR_EXIT("signal error");
    
        g_data = zeros;//默认赋值
        alarm(1);
        for (;;)
        {
            g_data = zeros;
            g_data = ones;//不断对其进行赋值,正常情况应该要不是zeros,要不就是ones
        }
        return 0;
    }
    
    void handler(int sig)
    {
        printf("%d %d
    ", g_data.a, g_data.b);//打印出值,观察其输出可以看到不可重录函数的缺点
        alarm(1);
    }

    编译运行:

    这是为什么呢?

    这是由于有一个全局变量g_data,而且在for循环中不断进行赋值,

    由于赋值不是一个原子操作,拿g_data=zeros这个赋值操作来说,由两部组成:

    ①g_data.a = zeros.a;

    ②g_data.b = zeros.b

    如果g_data之前的值为ones,当执行到第①步赋值操作时,信号来了终止了第②步赋值操作,那处理handler打印时,则会打印出0,1,因为第二个赋值操作停止了,造成了只赋值了一部分,所以上面程序的打印结果就可以解释了,将handler中的打印语句提取到一个新的函数中:
    导致不可重录函数的原因,是由于:
    中断之前的处理程序跟中断程序访问了一些共享数据g_data,
    【说明】:如果此处不访问g_data的话,也就不会产生不可重录的问题。
    所以导致不可重录只要有以下几个方面:
    那一些函数才算是安全可以在信号处理函数中使用呢?查看man帮助:
    除了这些函数是安全的之外,其它的都是不安全的,所以说使用信号是很容易出错的,现在的内核也正在考虑有没有一个机制来替换信号,实际上正在考虑能否用文件描述符来替换信号,这正是下个内核要实现的功能,将信号融入到文件描述上进行处理。
     
  • 相关阅读:
    WCF技术剖析之二十三:服务实例(Service Instance)生命周期如何控制[上篇]
    WCF技术剖析之二十二: 深入剖析WCF底层异常处理框架实现原理[上篇]
    WCF技术剖析之十七:消息(Message)详解(中篇)
    WCF技术剖析之十:调用WCF服务的客户端应该如何进行异常处理
    如何通过自定义MessageFilter的方式利用按键方式操作控件滚动条[附源代码]
    WCF技术剖析之十九:深度剖析消息编码(Encoding)实现(上篇)
    WCF技术剖析之十九:深度剖析消息编码(Encoding)实现(下篇)
    好书推荐:最终PDF版本的 《Microsoft Application Architecture Guide, Second Edition》
    WCF技术剖析之二十三:服务实例(Service Instance)生命周期如何控制[中篇]
    CRUD is bad for REST
  • 原文地址:https://www.cnblogs.com/webor2006/p/3744002.html
Copyright © 2011-2022 走看看