更改进程的信号屏蔽字可以阻塞所选择的信号,或解除对它们的阻塞。使用这种技术可以保护不希望由信号中断的代码临界区。如果希望对一个信号解除阻塞,然后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之前的值。

注意,此函数没有成功返回值。如果它返回到调用者,则总是返回-1,并将errno设置为EINTR(表示一个被中断的系统调用)。

实例

程序清单10-15显示了保护临界区,使其不被特定信号中断的正确方法。

程序清单10-15 保护临界区不被信号中断

#include "apue.h"

static void sig_int(int);

int
main(void)
{
sigset_t newmask, oldmask, waitmask; pr_mask("program start: "); if(signal(SIGINT, sig_int) == SIG_ERR)
err_sys("signal(SIGINT) error");
sigemptyset(&waitmask);
sigaddset(&waitmask, SIGUSR1);
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.
*/
pr_mask("in critical region: "); /*
* Pause, allowing all signals except SIGUSR1.
*/
if(sigsuspend(&waitmask) != -1)
err_sys("sigsuspend error"); pr_mask("after return from sigsuspend: "); /*
* Reset signal mask which unblocks SIGINT.
*/
if(sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
err_sys("SIG_SETMASK error"); /*
* And continue processing...
*/
pr_mask("program exit: "); exit(0);
} static void
sig_int(int signo)
{
pr_mask("\nin sig_int: ");
}

运行该程序的结果如下:

此结果与《UNIX环境高级编程》中的结果不一致,不同之处在于,in sig_int:SIGINT SIGUSR1。(在处理某一信号的时候会不会把该信号暂时阻塞直到对该信号的信号处理函数返回。)这里的问题与http://www.cnblogs.com/nufangrensheng/p/3516134.html中最后的问题其实是一个性质的问题。我想可能是由于实现版本不同造成的吧。

 

实例

sigsuspend的另一种应用是等待一个信号处理程序设置一个全局变量。程序清单10-16用于捕捉中断信号和退出信号,但是希望仅当捕捉到退出信号时,才唤醒主例程。

程序清单10-16 用sigsuspend等待一个全局变量被设置

#include "apue.h"

volatile sig_atomic_t    quitflag;    /* set nonzero by signal handler */

static void
sig_int(int signo) /* one signal handler for SIGINT and SIGQUIT */
{
signal(SIGINT, sig_int);
signal(SIGQUIT, sig_int);

if (signo == SIGINT)
printf("\ninterrupt\n");
else if (signo == SIGQUIT)
quitflag = 1; /* set flag for main loop */
} int
main(void)
{
sigset_t newmask, oldmask, zeromask; if(signal(SIGINT, sig_int) == SIG_ERR)
err_sys("signal(SIGINT) error");
if(signal(SIGQUIT, sig_int) == SIG_ERR)
err_sys("signal(SIGQUIT) error"); sigemptyset(&zeromask);
sigemptyset(&newmask);
sigaddset(&newmask, SIGQUIT); /*
* Block SIGQUIT and save current signal mask.
*/
if(sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
err_sys("SIG_BLOCK error"); while(quitflag == 0)
{
sigsuspend(&zeromask);
} /*
* SIGQUIT has been caught and is now blocked; do whatever.
*/
quitflag = 0; /*
* Reset signal mask which unblocks SIGQUIT.
*/
if(sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
err_sys("SIG_SETMASK error"); exit(0);
}

注意,程序清单10-16中标记为红色字体的两行在《UNIX环境高级编程》中本来是没有的,但是不加这两行,在我的运行环境下(Red Hat Linux 2.6.18)得不到跟书中一样的运行结果:

书中的运行结果(可以反复键入中断字符):

我的运行结果(键入一次中断字符后,第二次键入中断字符就退出了):

而在程序中加入上面带红色标记的语句后,则可以得到与书中同样的结果。

出现上面现象的原因是:对于书中的UNIX版本:一旦对给定的信号设置了一个动作,那么在调用sigaction(signal函数的实现其实就是调用sigaction)显式地改变它之前,该设置就一直有效。而对于我的版本:在进程每次接到信号对其进行处理时,随即将该信号动作复位为默认值(早期版本)。详情可参考:http://www.cnblogs.com/nufangrensheng/p/3515945.html

 

实例

可以用信号实现父、子进程之间的同步,这是信号应用的另一个实例。程序清单10-17包含了http://www.cnblogs.com/nufangrensheng/p/3510306.html中提到的五个例程的实现,它们是:TELL_WAIT、TELL_PARENT、TELL_CHILD、WAIT_PARENT和WAIT_CHILD。

程序清单10-17 父子进程可用来实现同步的例程

#include "apue.h"

static volatile sig_atomic_t sigflag;    /* set nonzero by sig handler */
static sigset_t newmask, oldmask, zeromask; static void
sig_usr(int signo) /* one signal handler for SIGUSR1 and SIGUSR2 */
{
sigflag = 1;
} void
TELL_WAIT(void)
{
if (signal(SIGUSR1, sig_usr) == SIG_ERR)
err_sys("signal(SIGUSR1) error");
if (signal(SIGUSR2, sig_usr) == SIG_ERR)
err_sys("signal(SIGUSR2) error");
sigemptyset(&zeromask);
sigemptyset(&newmask);
sigaddset(&newmask, SIGUSR1);
sigaddset(&newmask, SIGUSR2); /*
* Block SIGUSR1 and SIGUSR2, and save current signal mask.
*/
if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
err_sys("SIG_BLOCK error");
} void
TELL_PARENT(pid_t pid)
{
kill(pid, SIGUSR2); /* tell parent we're done */
} void
WAIT_PARENT(void)
{
while(sigflag == 0)
sigsuspend(&zeromask); /* and wait for parent */
sigflag = 0; /*
* Reset signal mask to original value.
*/
if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
err_sys("SIG_SETMASK error");
} void
TELL_CHILD(pid_t pid)
{
kill(pid, SIGUSR1);
} void
WAIT_CHILD(void)
{
while(sigflag == 0)
sigsuspend(&zeromask); /* and wait for child */
sigflag = 0; /*
* Reset signal mask to original value.
*/
if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
err_sys("SIG_SETMASK error");
}

其中使用了两个用户定义的信号:SIGUSR1由父进程发送给子进程,SIGUSR2由子进程发送给父进程。

如果在等待信号发生时希望去休眠,则使用sigsuspend函数是非常合适的,但是如果在等待信号期间希望调用其他系统函数,那么将会怎样呢?不幸的是,在单线程环境下对此问题没有妥善的解决方法。如果可以使用多线程,则可专门安排一个线程处理信号。

如果不使用线程,那么我们能尽力做到最好的是,当信号发生时,在信号捕捉程序中对一个全局变量置1.

 

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

信号之sigsuspend函数的更多相关文章

  1. 【C】——利用sigsuspend函数等待信号阻塞进程

    #include<signal.h> int sigsuspend(const sigset_t *sigmask); 返回值:-,并将errno设置为EINTR 将进程的信号屏蔽字设置为 ...

  2. UNIX环境高级编程——sigqueue、sigsuspend函数

    一.sigqueue函数 功能:新的发送信号系统调用,主要是针对实时信号提出的支持信号带有参数,与函数sigaction()配合使用. int sigqueue(pid_t pid, int sig, ...

  3. Linux 信号signal处理函数

    转自:http://www.cnblogs.com/taobataoma/archive/2007/08/30/875662.html alarm(设置信号传送闹钟) 相关函数 signal,slee ...

  4. 竞态条件与sigsuspend函数

    一.利用pause和alarm函数实现sleep函数 #include <unistd.h> int pause(void); pause函数使调用进程挂起直到有信号递达.如果信号的处理动 ...

  5. Linux 信号signal处理函数--转

    alarm(设置信号传送闹钟)相关函数 signal,sleep 表头文件 #include<unistd.h> 定义函数 unsigned int alarm(unsigned int ...

  6. linux c编程:信号(五) sigsuspend

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

  7. sigprocmask , sigpending 和 sigsuspend函数

    转自 http://blog.csdn.net/elbort/article/details/7594772 sigprocmask函数:功能描述:设定对信号屏蔽集内的信号的处理方式(阻塞或不阻塞). ...

  8. Qt之自定义信号和槽函数

    自定义信号和槽函数: 1.类的声明和实现分别放在.h和.cpp文件中: 2.类声明包含Q_OBJECT宏: 3.信号只要声明不要设计其的实现函数 4.发射信号用emit关键字 5.自定义槽的实现与普通 ...

  9. qt的信号与槽函数

    关联: bool connect ( const?QObject?*?sender, const?char?*?signal, const QObject * receiver, const char ...

随机推荐

  1. Spring中的事务管理

    事务简介: 事务管理是企业级应用程序开发中必不可少的技术,用来确保数据的完整性和一致性 事务就是一系列的动作,它们被当作一个单独的工作单元.这些动作要么全部完成,要么全部不起作用 事务的四个关键属性( ...

  2. Spring AOP--基于XML文件的配置

    Spring AOP的配置可以基于注解,也可以基于XML文件.前面几篇都是使用注解的方式.下面介绍下使用XML文件如何配置 使用的测试类和切面类都类似.只需要属于AOP的注解去掉即可.下面是AOP的X ...

  3. java.lang和java.lang.annotation中实现Annotation的类小结

    加了注解,等于打上了某种标记,没加,则等于没有某种标记,以后,其他程序可以用反射来了解你的类上面有无何种标记,看你有什么标记,就去干相应的事.标记可以加在类,方法,字段,包上,方法的参数上. (1)  ...

  4. 27、Service

    1服务可以通过startservice的方法 开启.通过stopservice的方法 停止. 服务有一个特点: 只会一次onCreate()方法一旦被创建出来,以后oncreate() 就不会再被执行 ...

  5. jquery选择器返回值

    jquery选择器$('selector')返回的不是数组,而是封装好的jquery对象.但这个对象有一个特别的地方,就是查询到的节点被以下标为属性,添加到了jquery对象上,所以它看起来像数组,因 ...

  6. LWIP互联网资料汇总

    本文主要搜集了下互联网上关于LWIP的资料和教程 欢迎补充 第一部分:移植 LWIP在UCOS上移植 LWIP 在STM32上移植   http://www.docin.com/p-459242028 ...

  7. 利用python进行折线图,直方图和饼图的绘制

    我用10个国家某年的GDP来绘图,数据如下: labels   = ['USA', 'China', 'India', 'Japan', 'Germany', 'Russia', 'Brazil', ...

  8. java@ 利用ArrayList实现dijkstra算法以及topological 排序算法(java.util.ArrayList)

    package dataStructure; import java.util.ArrayList; import java.util.LinkedList; import java.util.Que ...

  9. BNUOJ-26474 Bread Sorting 逆序对

    题目链接:http://www.bnuoj.com/bnuoj/problem_show.php?pid=26474 题意:给一个数列,可以对三个数操作:把最后一个数放到第一个,前两个数后移一位.问最 ...

  10. Django中的Model(操作表)

    Model 操作表 一.基本操作 # 增 models.Tb1.objects.create(c1='xx', c2='oo') #增加一条数据,可以接受字典类型数据 **kwargs obj = m ...