zoukankan      html  css  js  c++  java
  • linux c编程:信号(五) sigsuspend

    更改进程的信号屏蔽字可以阻塞所选择的信号,或解除对它们的阻塞。使用这种技术可以保护不希望由信号中断的代码临界区。如果希望对一个信号解除阻塞,然后pause等待以前被阻塞的信号发生,则又将如何呢?假定信号时SIGINT,实现这一点的一种不正确的方法是:

    sigset_t    newmask, oldmask;

     

    sigemptyset(&newmask);

    sigaddset(&newmask, SIGINT);

    /* block SIGINT and save current signal mask */if(sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)

        err_sys("SIG_BLOCK error");

    /* critical region of code */if(sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)

        err_sys("SIG_SETMASK error");

    /* window is open */

    pause();    /* wait for signal to occur */

    /* continue processing */

    如果在信号阻塞时将其发送给进程,那么该信号的传递就被推迟直到对它解除了阻塞。对应用程序而言,该信号好像发生在解除对SIGINT的阻塞和pause之间。如果发生了这种情况,或者如果在解除阻塞时刻和pause之间确实发生了信号,那么就产生了问题。因为我们可能不会再见到该信号,所以从这种意义上而言,在此时间窗口(解除阻塞和pause之间)中发生的信号丢失了,这样就使pause永远阻塞。

    为了纠正此问题,需要在一个原子操作中先恢复信号屏蔽字,然后使进程休眠。这种功能是由sigsuspend函数提供的。

    #include <signal.h>

    int sigsuspend( const sigset_t *sigmask );

    返回值:-1,并将errno设置为EINTR

     

    将进程的信号屏蔽字设置为由sigmask指向的值。在捕捉到一个信号或发生了一个会终止该进程的信号之前,该进程被挂起。如果捕捉到一个信号而且从该信号处理程序返回,则sigsuspend返回,并且将该进程的信号屏蔽字设置为调用sigsuspend之前的值。

    #include <unistd.h>

    #include <signal.h>

    #include <stdio.h>

    void handler(int sig)   //信号处理程序

    {

       if(sig == SIGINT)

          printf("SIGINT sig");

       else if(sig == SIGQUIT)

          printf("SIGQUIT sig");

       else

          printf("SIGUSR1 sig");

    }

     

    int main()

    {

        sigset_t new,old,wait;   //三个信号集

        struct sigaction act;

        act.sa_handler = handler;

        sigemptyset(&act.sa_mask);

        act.sa_flags = 0;

        sigaction(SIGINT, &act, 0);    //可以捕捉以下三个信号:SIGINT/SIGQUIT/SIGUSR1

        sigaction(SIGQUIT, &act, 0);

        sigaction(SIGUSR1, &act, 0);

       

        sigemptyset(&new);

        sigaddset(&new, SIGINT);  //SIGINT信号加入到new信号集中

        sigemptyset(&wait);

        sigaddset(&wait, SIGUSR1);  //SIGUSR1信号加入wait

        sigprocmask(SIG_BLOCK, &new, &old);       //SIGINT阻塞,保存当前信号集到old

       

        //临界区代码执行    

      

        if(sigsuspend(&wait) != -1)  //程序在此处挂起;用wait信号集替换new信号集。即:过来SIGUSR1  号,阻塞掉,程序继续挂起;过来其他信号,例如SIGINT,则会唤醒程序。执行sigsuspend的原子操作。注意:如果“sigaddset(&wait, SIGUSR1);”这句没有,则此处不会阻塞任何信号,即过来任何信号均会唤醒程序。

            printf("sigsuspend error");

        printf("After sigsuspend");

        sigprocmask(SIG_SETMASK, &old, NULL);

        return 0;

    }

    sigsuspend的原子操作是:

    1)设置新的mask阻塞当前进程(上面是用wait替换new,即阻塞SIGUSR1信号)

    2)收到SIGUSR1信号,阻塞,程序继续挂起;收到其他信号,恢复原先的mask(即包含SIGINT信号的)

    3)调用该进程设置的信号处理函数(程序中如果先来SIGUSR1信号,然后过来SIGINT信号,则信号处理函数会调用两次,打印不同的内容。第一次打印SIGINT,第二次打印SIGUSR1,因为SIGUSR1是前面阻塞的)

    4)待信号处理函数返回,sigsuspend返回了。(sigsuspend将捕捉信号和信号处理函数集成到一起了)

     

    sigsuspend的另一种应用是等待一个信号处理程序设置一个全局变量,下面的这个程序用于捕捉中断信号和退出信号。但是只是希望在捕捉到退出信号的时候,才唤醒主例程

     

    int quitflag;

     

    void sig_int(int signo){

     

        if(signo == SIGINT){

     

            printf("  interrupt  ");

     

    }

     

    //当收到退出信号的时候,全局变量置为1

     

        else if(signo == SIGQUIT){

     

            quitflag == 1;

     

        }

     

    }

     

     

     

    void suspend_function(){

     

    sigset_t newmask,oldmask,zeromask;

     

    //设置中断和退出的信号处理。

     

        signal(SIGINT,sig_int);

     

        signal(SIGQUIT,sig_int);

     

        sigemptyset(&zeromask);

     

        sigemptyset(&newmask);

     

        sigaddset(&newmask,SIGQUIT);

     

        sigprocmask(SIG_BLOCK,&newmask,&oldmask);

     

        while(quitflag == 0)

     

            sigsuspend(&zeromask);

     

        quitflag=1;

     

        sigprocmask(SIG_SETMASK,&oldmask,NULL);

     

        exit(0);

     

    }

     

    程序结果如下:

  • 相关阅读:
    ASP.NET- 查找Repeater控件中嵌套的控件
    Oracle- 表的管理
    Oracle- PL/SQL DEV工具的使用收集
    Oracle- PL/SQL DEV的远程配置
    Oracle- 提示查询结果不可更新,请使用...更新结果。
    Oracle- 存储过程和异常捕捉
    MSSQLSERVER数据库- SP_EXECUTESQL的使用
    Oracle- 用户管理
    Oracle- 初识
    c语言交换两个变量的值
  • 原文地址:https://www.cnblogs.com/zhanghongfeng/p/9153060.html
Copyright © 2011-2022 走看看