Linux信号实例

来源:岁月联盟 编辑:exp 时间:2011-09-17

SA_RESTART
-----------------------------------------
设置信号S的SA_RESTART属性, 如果系统调用被信号S中断, 当信号处理函数返回后,
将自动恢复该系统调用

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <error.h>
#include <string.h>

void sig_handler(int signum)
{
    printf("at handler/n");
    sleep(10);
    printf("leave handler/n");
}

int main(int argc, char **argv)
{
    char buf[100];
    int ret;
    struct sigaction action, old_action;

    action.sa_handler = sig_handler;
    sigemptyset(&action.sa_mask);
    action.sa_flags = 0;
    action.sa_flags |= SA_RESTART;  /* 设置或者不设置此属性 */

    sigaction(SIGINT, NULL, &old_action);
    if (old_action.sa_handler != SIG_IGN) {
        sigaction(SIGINT, &action, NULL);
    }

    bzero(buf, 100);

    ret = read(0, buf, 100);
    if (ret == -1) {
        perror("read");
    }

    printf("read %d bytes:/n", ret);
    printf("%s/n", buf);

    return 0;
}

若不设置, 从标准输入读数据的时候,如果按下ctrl+c, 则read系统调用被中断, 返回-1
若设置, 信号处理函数返回后, 恢复read系统调用, 但信号处理返回之前, read是无法读取数据的


int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set, int signum);
int sigdelset(sigset_t *set, int signum);
int sigismember(const sigset_t *set, int signum);
int sigsuspend(const sigset_t *mask);
int sigpending(sigset_t *set);

屏蔽进程的某些信号
-------------------------------------
所谓屏蔽, 并不是禁止递送信号, 而是暂时阻塞信号的递送,
解除屏蔽后, 信号将被递送, 不会丢失

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <error.h>
#include <string.h>

void sig_handler(int signum)
{
    printf("catch sigint/n");
}

int main(int argc, char **argv)
{
    sigset_t block;
    struct sigaction action, old_action;

    action.sa_handler = sig_handler;
    sigemptyset(&action.sa_mask);
    action.sa_flags = 0;

    sigaction(SIGINT, NULL, &old_action);
    if (old_action.sa_handler != SIG_IGN) {
        sigaction(SIGINT, &action, NULL);
    }

    sigemptyset(&block);
    sigaddset(&block, SIGINT);

    printf("block sigint/n");
    sigprocmask(SIG_BLOCK, &block, NULL);

    sleep(5);

    /* unblock信号后, 之前触发的信号将被递送, 如果之前被
     * 触发多次, 只会递送一次 */
    sigprocmask(SIG_UNBLOCK, &block, NULL);
    printf("unblock sigint/n");

    sleep(5);

    return 0;
}

在信号处理函数内, 屏蔽某些信号
----------------------------------------
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <error.h>
#include <string.h>

void sig_handler(int signum)
{
    printf("in handle...sigterm is blocked/n");
    sleep(5);
    printf("handle done/n");
}

void handle_term(int signum)
{
    printf("catch sigterm and exit../n");
    exit(0);
}

int main(int argc, char **argv)
{
    struct sigaction action, old_action;

    /* 设置SIGINT */
    action.sa_handler = sig_handler;
    sigemptyset(&action.sa_mask);
    sigaddset(&action.sa_mask, SIGTERM);
    action.sa_flags = 0;

    sigaction(SIGINT, NULL, &old_action);
    if (old_action.sa_handler != SIG_IGN) {
        sigaction(SIGINT, &action, NULL);
    }

    /* 设置SIGTERM */
    action.sa_handler = handle_term;
    sigemptyset(&action.sa_mask);
    action.sa_flags = 0;

    sigaction(SIGTERM, NULL, &old_action);
    if (old_action.sa_handler != SIG_IGN) {
        sigaction(SIGTERM, &action, NULL);
    }

    printf("pid = %d/n", getpid());

    while (1) {
        sleep(1);
    }

    return 0;
}
按下ctrl+c, 收到SIGINT后, 进入sig_handler, 阻塞掉SIGTERM, 此时
kill掉该进程, 程序收不到SIGTERM信号, 等sig_handler返回后, 才收到
SIGTERM信号, 然后退出程序


int setjmp(jmp_buf env);
int sigsetjmp(sigjmp_buf env, int savesigs);
void longjmp(jmp_buf env, int val);
void siglongjmp(sigjmp_buf env, int val);
--------------------------------------------------------
setjmp()会保存目前堆栈环境,然后将目前的地址作一个记号,而在程序其他地方调用
longjmp时便会直接跳到这个记号位置,然后还原堆栈,继续程序好执行。

setjmp调用有点fork的味道, setjmp()return 0 if returning directly,
and non-zero when returning from longjmp using  the saved context.

if (setjmp(jmpbuf)) {
   printf("return from jmp/n");
} else {
   printf("return directly/n");
}

setjmp和sigsetjmp的唯一区别是: setjmp不一定会恢复信号集合, 而sigsetjmp可以
保证恢复信号集合

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <setjmp.h>

void sig_alrm(int signum);
void sig_usr1(int signum);
void print_mask(const char *str);

static sigjmp_buf jmpbuf;
static volatile sig_atomic_t canjmp;
static int sigalrm_appear;

int main(int argc, char **argv)
{
    struct sigaction action, old_action;

    /* 设置SIGUSR1 */
    action.sa_handler = sig_usr1;
    sigemptyset(&action.sa_mask);
    action.sa_flags = 0;

    sigaction(SIGUSR1, NULL, &old_action);
    if (old_action.sa_handler != SIG_IGN) {
        sigaction(SIGUSR1, &action, NULL);
    }

    /* 设置SIGALRM */
    action.sa_handler = sig_alrm;
    sigemptyset(&action.sa_mask);
    action.sa_flags = 0;

    sigaction(SIGALRM, NULL, &old_action);
    if (old_action.sa_handler != SIG_IGN) {
        sigaction(SIGALRM, &action, NULL);
    }

    printf("pid = %d/n", getpid());
    print_mask("starting main:");

    sleep(10);

    if (sigsetjmp(jmpbuf, 1) != 0) {
        print_mask("ending main:");
    } else {
        printf("setjmp return/n");
        canjmp = 1;
        while (1) {
            sleep(1);
        }
    }

    return 0;
}

void sig_usr1(int signum)
{
    time_t starttime;
    if (canjmp == 0) {
        printf("please set jmp first/n");
        return;
    }

    print_mask("starting sig_usr1:");

    alarm(3);
    while (!sigalrm_appear);
    print_mask("finishing sig_usr1:");
    canjmp = 0;
    siglongjmp(jmpbuf, 1);
}

void sig_alrm(int signum)
{
    print_mask("in sig_alrm:");
    sigalrm_appear = 1;

    return;
}

void print_mask(const char *str)
{
    sigset_t sigset;
    int i, errno_save, flag = 0;

    errno_save = errno;

    if (sigprocmask(0, NULL, &sigset) < 0) {
        printf("sigprocmask error/n");
        exit(0);
    }

    printf("%s/n", str);
    fflush(stdout);

    for (i = 1; i < NSIG; i++) {
        if (sigismember(&sigset, i)) {
            flag = 1;
            psignal(i, "blocked");
        }
    }

    if (!flag) {
        printf("no blocked signal/n");
    }

    printf("/n");
    errno = errno_save;
}

程序运行后, 使用 kill -USR1 <pid> 发送信号
程序收到SIGUSR1, 进入sig_usr1, 自动屏蔽掉SIGUSR1信号, 然后开启一个
定时器, 到时间后, 产生SIGALRM信号, 进入sig_alrm, 此时自动屏蔽SIGUSR1和SIGALRM,
返回到main函数后, 信号屏蔽恢复原状

作者“kenby”