10(信号)

1 signal函数

#include <signal.h>
void (*signal(int signo, void (*func)(int)))(int); 
        Returns: previous disposition of signal (see following) if OK, SIG_ERR on error

func的值可以是:
(1)SIG_IGN–忽略
(2)SIG_DFL–系统默认动作
(3)调用的函数地址–信号处理程序

#include "apue.h"
static void sig_usr(int);   /* one handler for both signals */
Int main(void)
{
    if (signal(SIGUSR1, sig_usr) == SIG_ERR)
        err_sys("can't catch SIGUSR1");
    if (signal(SIGUSR2, sig_usr) == SIG_ERR)
        err_sys("can't catch SIGUSR2");
    for ( ; ; )
        pause();//接到信号时挂起
}
static void sig_usr(int signo)      /* argument is signal number */
{
    if (signo == SIGUSR1)
        printf("received SIGUSR1\n");
    else if (signo == SIGUSR2)
        printf("received SIGUSR2\n");
    else
        err_dump("received signal %d\n", signo);
}

10(信号)_第1张图片

2 kill和raise函数

kill把信号发送给进程或进程组;
raise把信号发送给(进程)自身.

#include <signal.h>
int kill(pid_t pid, int signo);
int raise(int signo);
        Both return: 0 if OK, 1 on error

raise(signo);等价于kill(getpid(), signo);

kill函数中的pid参数, 它有以下4种情况:
pid > 0: 将该信号发送给进程ID为pid的进程.
pid == 0: 将该信号发送给与发送进程属于同一进程组的所有进程(不包括内核进程和init进程). 此时, 发送进程必须具有向这些进程发送信号的权限.
pid < 0: 将该信号发给其进程组ID等于pid绝对值的所有进程(不包括内核进程和init进程). 此时, 发送进程必须具有向这些进程发送信号的权限.
pid == -1: 将该信号发送给发送进程有权限向它们发送信号的系统上的所有进程.(不包括内核进程和init进程).

3 alarm和pause函数

alarm函数是设置一个计时器, 在计时器超时的时候, 产生SIGALRM信号. 如果不忽略或捕捉此信号, 它的默认操作是终止调用该alarm函数的进程.
alarm的返回值是无符号整型, 每个进程只能有一个alarm维护的”闹钟”.
如果该”闹钟”顺利超时, 则返回0;
如果该”闹钟”在计时过程中, 调用了另一个alarm函数, 则该”闹钟”的余留秒数作为该次alarm的返回值, 并且新的”闹钟”开始计时.(实际上是新的闹钟替代了以前的闹钟)

#include <unistd.h>
unsigned int alarm(unsigned int seconds);
        Returns: 0 or number of seconds until previously set alarm

pause函数将进程挂起,直到捕捉到一个信号

#include <unistd.h>
int pause(void);
            Returns: 1 with errno set to EINTR 

4 信号集

#include <signal.h>
int sigemptyset(sigset_t *set);//清除所有信号
int sigfillset(sigset_t *set);//初始化信号集,使其包括所有信号
int sigaddset(sigset_t *set, int signo);//往set中添加signo
int sigdelset(sigset_t *set, int signo);//从set中移除signo
        All four return: 0 if OK, 1 on error 
int sigismember(const sigset_t *set, int signo);
        Returns: 1 if true, 0 if false, 1 on error

所有应用程序在使用信号集前都要调用sigemptyset或者sigfillset初始化

5 sigprocmask函数

用于改变进程的当前阻塞信号集,也可以用来检测当前进程的信号掩码。

#include <signal.h>
int sigprocmask(int how, const sigset_t *restrict set, sigset_t *restrict oset);
        Returns: 0 if OK, 1 on error

如果参数oldset不是NULL指针,那么目前的信号屏蔽字会由此指针返回
如果set是一个非空指针,则参数how指示如何修改当前信号屏蔽字

参数how的取值不同,带来的操作行为也不同,该参数可选值如下:
1.SIG_BLOCK: 该值代表的功能是将set所指向的信号集中所包含的信号加到当前的信号掩码中,作为新的信号屏蔽字oset。
2.SIG_UNBLOCK:将参数set所指向的信号集中的信号从当前的信号掩码中移除。
3.SIG_SETMASK:设置当前信号掩码为参数set所指向的信号集中所包含的信号。

6 sigpending函数

sigpending函数返回在送往进程的时候被阻塞挂起的信号集合。这个信号集合通过参数set返回。

#include <signal.h>
int sigpending(sigset_t *set);
        Returns: 0 if OK, 1 on error
#include "apue.h"
static void sig_quit(int);
Int main(void)
{
    sigset_t    newmask, oldmask, pendmask;

    if (signal(SIGQUIT, sig_quit) == SIG_ERR)
        err_sys("can't catch SIGQUIT");
    /* * Block SIGQUIT and save current signal mask. */
    sigemptyset(&newmask);
    sigaddset(&newmask, SIGQUIT);
    if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)//并集,通过oldmask返回
        err_sys("SIG_BLOCK error");

    sleep(5);           //要过5秒才会调用signal(SIGQUIT, sig_quit)
    if (sigpending(&pendmask) < 0)//通过pendmask返回信号集
        err_sys("sigpending error");
    if (sigismember(&pendmask, SIGQUIT))
        printf("\nSIGQUIT pending\n");

    /* * Reset signal mask which unblocks SIGQUIT. */
    if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
        err_sys("SIG_SETMASK error");
    printf("SIGQUIT unblocked\n");

    sleep(5);   /* SIGQUIT here will terminate with core file */
    exit(0);
}
static void sig_quit(int signo)
{
    printf("caught SIGQUIT\n");
    if (signal(SIGQUIT, SIG_DFL) == SIG_ERR)
        err_sys("can't reset SIGQUIT");
}

7 sigsetjmp和siglongjmp函数

信号处理程序中的非局部转移:

#include <setjmp.h>
int sigsetjmp(sigjmp_buf env, int savemask);
        Returns: 0 if called directly, nonzero if returning from a call to siglongjmp 
void siglongjmp(sigjmp_buf env, int val);

sigsetjmp和siglongjmp使用基本相同,只是siglongjmp比longjmp多一个参数,若savemask为非0值,则sigsetjmp在env中保存进程的当前屏蔽字。若调用siglongjmp时,如果带非0 savemask的sigsetjmp调用已经保存了env,则siglongjmp从其中回复保存的信号屏蔽字。

8 sigsuspend函数

sigsuspend用于在接收到某个信号之前,临时用mask替换进程的信号掩码,并暂停进程执行,直到收到信号为止。
也就是说,进程执行到sigsuspend时,sigsuspend并不会立刻返回,进程处于TASK_INTERRUPTIBLE状态并立刻放弃CPU,等待UNBLOCK(mask之外的)信号的唤醒。进程在接收到UNBLOCK(mask之外)信号后,调用处理函数,然后把现在的信号集还原为原来的,sigsuspend返回,进程恢复执行。

#include <signal.h>
int sigpending(sigset_t *set);
        Returns: 0 if OK, 1 on error

实例一:保护临界区,使其不被特定的信号中断的正确方法

#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: ");
}

10(信号)_第2张图片
实例二:等待一个信号处理程序设置一个全局变量

#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 */
{
    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);//zeromask为空。等待任意信号
         //捕捉到SIGINT只是打印“interrupt”;捕捉到SIGQUIT才会置为1,往下走
     /* * 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);
}

9 sleep函数的有效实现

#include "apue.h"

static void sig_alrm(int signo)
{
    /* nothing to do, just returning wakes up sigsuspend() */
}

unsigned int sleep(unsigned int nsecs)
{
    struct sigaction    newact, oldact;
    sigset_t            newmask, oldmask, suspmask;
    unsigned int        unslept;

    /* set our handler, save previous information */
    newact.sa_handler = sig_alrm;
    sigemptyset(&newact.sa_mask);
    newact.sa_flags = 0;
    sigaction(SIGALRM, &newact, &oldact);//sigaction是一个函数,可以用来查询或设置信号处理方式。
    //给信号signum设置新的信号处理函数act, 同时保留该信号原有的信号处理函数oldact
    //相当于之前见到的if(signal(...))

    /* block SIGALRM and save current signal mask */
    sigemptyset(&newmask);
    sigaddset(&newmask, SIGALRM);
    sigprocmask(SIG_BLOCK, &newmask, &oldmask);

    alarm(nsecs);

    suspmask = oldmask;
    sigdelset(&suspmask, SIGALRM);  /* make sure SIGALRM isn't blocked */
    sigsuspend(&suspmask);          /* wait for any signal to be caught */
    //sleep期间如果有信号则往下走,如果没有,等到alarm结束发送sig_alrm信号

    /* some signal has been caught, SIGALRM is now blocked */

    unslept = alarm(0);
    sigaction(SIGALRM, &oldact, NULL);  /* reset previous action */

    /* reset signal mask, which unblocks SIGALRM */
    sigprocmask(SIG_SETMASK, &oldmask, NULL);
    return(unslept);
}

你可能感兴趣的:(10(信号))