UNIX环境高级编程-第10章- 信号 - 二

10.15 sigsetjmp和siglongjmp 函数

7.10节介绍了两个关于用于非局部跳转的 setjmp 和 longjmp 函数,在信号处理程序中经常调用 longjmp 函数以返回到程序的主循环中,而不是从该处理程序返回。但是调用 longjmp 有一个问题,当捕捉到一个信号时,进入信号捕捉函数,此时当前信号被自动加到进程的信号屏蔽字中。这阻止了后来产生的这种信号中断该信号处理程序。POSIX.1 并没有说明 setjmp 和 longjmp 对信号屏蔽字的作用,而是定义了两个新函数 sigsetjmp和siglongjmp。在信号处理程序进行非局部转移时应该使用这两个函数。

        在 sigsetjmp 中增加了一个参数,如果 savemask 非0,则 sigsetjmp 在 env 中保存进程的当前信号屏蔽字。调用 siglongjmp 时,如果带非0 savemask 的 sigsetjmp 调用已经保存了 env,则 siglongjmp 从其中恢复保存的信号屏蔽字。

#include <setjmp.h>  
int sigsetjmp(sigjmp_buf env, int savemask); //若直接调用则返回0,若从siglongjmp调用返回则返回非0值。  
void siglongjmp(sigjmp_buf env, int val);  

测试程序:

#include  "pr_mask.h"   /* include the function of pr_mask() */  
#include <setjmp.h>  
#include <time.h>  
  
static void sig_usr1(int), sig_alrm(int);  
static sigjmp_buf   jmpbuf;  
static volatile sig_atomic_t canjump;  
  
int main(void)  
{  
    if(signal(SIGUSR1, sig_usr1) == SIG_ERR)  
        err_sys("signal(SIGUSR1) error");  
    if(signal(SIGALRM, sig_alrm) == SIG_ERR)  
        err_sys("signal(SIGALRM) error");  
    pr_mask("starting main: ");  
  
    if(sigsetjmp(jmpbuf, 1))  
    {  
        pr_mask("ending main: ");  
        exit(0);  
    }  
    canjump = 1;  
    for(;;)  
        pause();  
}  
static void sig_usr1(int signo)  
{  
    time_t starttime;  
    if(0 == canjump)  
        return;  
    pr_mask("starting sig_usr1: ");  
    alarm(3);  
    starttime = time(NULL);  
  
    for(; ;)  
        if(time(NULL) > starttime+5)  
            break;  
    pr_mask("finishing sig_usr1: ");  
    canjump = 0;  
    siglongjmp(jmpbuf,1);  
}  
  
static void sig_alrm(int signo)  
{  
    pr_mask("in sig_alrm: ");  
}  

输出结果:

$ ./sigsetjmp &  
[1] 15206  
starting main:   
$ kill -USR1 15206  
starting sig_usr1:   
$ in sig_alrm:   
finishing sig_usr1:   
ending main:   
  
[1]+  Done                    ./sigsetjmp  

其中打印函数如下:

#ifndef PR_MASK_H  
#define PR_MASK_H  
#include "apue.h"  
#include <errno.h>  
  
void pr_mask(const char *str)  
{  
    sigset_t sigset;  
    int errno_save;  
  
    errno_save = errno;  
    if(sigprocmask(0, NULL, &sigset) < 0)  
        err_sys("sigprocmask error");  
  
    printf("%s\n", str);  
    if(sigismember(&sigset, SIGINT))  
        printf("SIGINT\t");  
    if(sigismember(&sigset, SIGQUIT))  
        printf("SIGQUIT\t");  
    if(sigismember(&sigset, SIGUSR1))  
        printf("SIGUSR1\t");  
    if(sigismember(&sigset, SIGALRM))  
        printf("SIGALRM\t");  
}  
#endif  

10.16 sigsuspend 函数

       更改进程的信号屏蔽字可以阻塞所选择的信号,或解除对它们的阻塞,使用这种技术可以保护不希望由信号中断的代码临界区。如果希望对一个信号解除阻塞,然后pause 等待以前被阻塞的信号发生,就是把“解除信号屏蔽”和“挂起等待信号”这两步能合并成一个原子操作。sigsuspend 函数能够实现该功能,即包含了pause的挂起等待功能,同时临时解除对某个信号的屏蔽

以下是该函数的原型:

#include <signal.h>  
int sigsuspend(const sigset_t *sigmask);//返回值:-1,并将errno设置为EINTR;  

        跟 pause 一样,sigsuspend 没有成功返回值,只有执行了一个信号处理函数之后sigsuspend 才返回,返回值为-1,并将errno 设置为 EINTR。 调用 sigsuspend  时,进程的信号屏蔽字由 sigmask  参数指定,可以通过指定 sigmask  来临时解除对某个信号的屏蔽,然后挂起等待,当 sigsuspend  返回时,进程的信号屏蔽字恢复为原来的值。

测试程序:

#include <signal.h>  
/* 
int sigsuspend(const sigset_t *sigmask);//返回值:-1,并将errno设置为EINTR; 
*/  
#include "apue.h"  
#include "pr_mask.h"  
  
static void sig_func(int signo);  
  
int main(void)  
{  
    sigset_t newmask, oldmask, waitmask;  
  
    pr_mask("Program start: ");  
  
    if(signal(SIGINT,sig_func) == SIG_ERR)  
        err_sys("signal error");  
    sigemptyset(&newmask);//初始化信号集  
    sigaddset(&newmask,SIGINT);//添加SIGINT信号  
  
    sigemptyset(&waitmask);  
    sigaddset(&waitmask,SIGUSR1);  
  
    //屏蔽信号  
    /* 
     * Block SIGINT and save current signal mask 
     */  
    if(sigprocmask(SIG_BLOCK,&newmask,&oldmask) < 0)  
        err_sys("SIG_BLOCK error");  
  
    pr_mask("in critical region: ");  
  
    //临时修改进程信号屏蔽字,在捕捉信号之前,将进程挂起等待  
    /* 
     * pause, allowing all of 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");  
  
    pr_mask("program exit: ");  
  
    exit(0);  
}  
  
static void sig_func(int signo)  
{  
    pr_mask("\nin sig_func: ");  
}  

输出结果:

$ ./sigsuspend  
Program start:   
in critical region: SIGINT    
^C  
in sig_func: SIGINT SIGUSR1   
after return from sigsuspend: SIGINT      
program exit:  

首先设置信号 SIGINT为信号屏蔽字进行阻塞;

当程序执行了 sigsuspend 后,临时使 SIGUSR1 成为信号屏蔽字进行阻塞,对 SIGINT 解除阻塞,并且挂起进程,等待信号输入;

我们在终端中输入ctrl-C ,即输入一个中断信号,进程捕捉到中断信号,调用捕捉函数sig_func 进行处理,处理完中断信号后sigsuspend 函数返回-1。此时,进程的信号屏蔽字恢复为原来的值SIGINT;

10.17 abort函数

  abort 函数的功能是使异常终止,此函数将SIGABRT 信号发送给调用进程,让进程捕捉 SIGABRT 信号目的是在进程终止之前由其执行所需的清理操作。默认情况是终止调用进程。该函数实现如下: 

#include <signal.h>  
#include <stdio.h>  
#include <stdlib.h>  
#include <unistd.h>  
  
void Mabort(void)  
{  
    sigset_t mask;  
    struct sigaction action;  
  
    /* 
     * caller can't ignore SIGABRT, if so reset to default. 
     */  
    sigaction(SIGABRT, NULL, &action);  
    if(action.sa_handler == SIG_IGN)  
    {  
        action.sa_handler = SIG_DFL;  
        sigaction(SIGABRT, &action, NULL);  
    }  
  
    if(action.sa_handler == SIG_DFL)  
        fflush(NULL);  
    /* 
     * caller can't block SIGABRT; make sure it's unblock 
     */  
    sigfillset(&mask);  
    sigdelset(&mask, SIGABRT);  /* mask has only SIGABRT turned off */  
    sigprocmask(SIG_SETMASK, &mask, NULL);  
    kill(getpid(), SIGABRT);    /* send the signal */  
  
    fflush(NULL);   /* flush all open stdio stream */  
    action.sa_handler = SIG_DFL;  
    sigaction(SIGABRT, &action, NULL);  /* reset to default */  
    sigprocmask(SIG_SETMASK, &mask, NULL);  /* just in case ... */  
    kill(getpid(), SIGABRT);    /* and one more time */  
    exit(1);    /* this should never be executed ... */  
}  
  
static void sig_abort(int signo)  
{  
    printf("Recevied abort ...\n");  
}  
int main(void)  
{  
    signal(SIGABRT, sig_abort);  
    Mabort();  
    pause();  
    exit(0);  
}  

输出结果:

Recevied abort ...  
Aborted (core dumped)  

10.18 system函数

  这里和之前介绍的system函数有区别,增加了信号处理机制。POSIX.1要求 system 函数忽略 SIGINT 和 SITQUIT 信号,阻塞 SIGCHLD。system 函数实现如下:

#include <stdio.h>  
#include <stdlib.h>  
#include <unistd.h>  
#include <sys/types.h>  
#include <errno.h>  
#include <signal.h>  
#include <sys/wait.h>  
  
int Msystem(const char*cmdstring)  
{  
    pid_t   pid;  
    int     status;  
    struct  sigaction   ignore,saveintr,savequit;  
    sigset_t    chldmask,savemask;  
  
    if(cmdstring == NULL)  
        return 1;  
    ignore.sa_handler = SIG_IGN;  
    sigemptyset(&ignore.sa_mask);  
    ignore.sa_flags = 0;  
    if(sigaction(SIGINT,&ignore,&savequit)<0)  
    {  
        perror("sigaction() error");  
        exit(-1);  
    }  
    if(sigaction(SIGQUIT,&ignore,&savequit) <0)  
    {  
        perror("sigaction() error");  
        exit(-1);  
    }  
    sigemptyset(&chldmask);  
    sigaddset(&chldmask,SIGCHLD);  
    if(sigprocmask(SIG_BLOCK,&chldmask,&savemask) < 0)  
    {  
        perror("sigprocmask() error");  
        exit(-1);  
    }  
    if((pid = fork()) == -1)  
    {  
        perror("fork() error");  
        exit(-1);  
    }  
    else if(pid == 0)  
    {  
        sigaction(SIGINT,&saveintr,NULL);  
        sigaction(SIGQUIT,&savequit,NULL);  
        sigprocmask(SIG_SETMASK,&savemask,NULL);  
        execl("/bin/sh","sh","-c",cmdstring,(char *)0);  
        _exit(-127);  
    }  
    else  
    {  
        while(waitpid(pid,&status,0) < 0)  
        {  
            if(errno != EINTR)  
            {  
                status = -1;  
                break;  
            }  
        }  
    }  
    if (sigaction(SIGINT,&saveintr,NULL)<0)  
        return -1;  
    if (sigaction(SIGQUIT,&saveintr,NULL)<0)  
        return -1;  
    if (sigprocmask(SIG_SETMASK,&savemask,NULL)<0)  
        return -1;  
    return (status);  
}  
  
int main()  
{  
    printf("Print date:\n");  
    Msystem("date");  
    printf("Print process:\n");  
    Msystem("ps");  
    exit(0);  
}  

输出结果:

[root@www chapter_10]# ./a.out
Print date:
2014年 12月 25日 星期四 15:44:54 CST
Print process:
  PIDTTY          TIME CMD
 4836pts/1    00:00:00 bash
 6699pts/1    00:00:00 a.out
 7340pts/1    00:00:00 a.out
 7342pts/1    00:00:00 ps

10.19 sleep函数

        此函数使调用进程被挂起,直到满足下列条件之一:

1.        已经过 seconds 所指定的墙上时钟时间;(返回0)

2.        调用进程捕捉到一个信号并从信号处理程序返回。(返回seconds – 实际挂起的时间)

sleep 函数的实现如下:

#include <stdio.h>  
#include <stdlib.h>  
#include <unistd.h>  
#include <sys/types.h>  
#include <errno.h>  
#include <signal.h>  
  
static void sig_alrm(int signo)  
{  
    printf("Recevied alarm ... \n");  
}  
  
unsigned int Msleep(unsigned int nsecs)  
{  
    struct sigaction    newact,oldact;  
    sigset_t            newmask,oldmask,suspmask;  
    unsigned int        unslept;  
  
    newact.sa_handler = sig_alrm;  
    sigemptyset(&newact.sa_mask);  
    newact.sa_flags = 0;  
    sigaction(SIGALRM,&newact,&oldact);  
    sigemptyset(&newmask);  
    sigaddset(&newmask,SIGALRM);  
    sigprocmask(SIG_BLOCK,&newmask,&oldmask);  
    alarm(nsecs);  
    suspmask = oldmask;  
    sigdelset(&suspmask,SIGALRM);  
    sigsuspend(&suspmask);  
    unslept = alarm(0);  
    sigprocmask(SIG_SETMASK,&oldmask,NULL);  
    return unslept;  
}  
  
int main()  
{  
    int i;  
    printf("Program starting.\n");  
    printf("sleep 5 seconds.....\n");  
    for(i=1;i<=5;++i)  
    {  
        printf("The %dth second.\n",i);  
        Msleep(1);  
    }  
    printf("wake up.\n");  
    exit(0);  
}  

输出结果:

[root@www chapter_10]# ./a.out
Program starting.
sleep 5 seconds.....
The 1th second.
Recevied alarm ...
The 2th second.
Recevied alarm ...
The 3th second.
Recevied alarm ...
The 4th second.
Recevied alarm ...
The 5th second.
Recevied alarm ...
wake up.
[root@www chapter_10]#


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值