LINUX_C编程实战-第九章《信号》学习笔记

1.信号:顾名思义,信号就是来传递信息的,传递的对象为进程(可用于进程间的通信);可由硬件或软件(使用kill、raise等)产生;终端下使用 kill -l 可以显示linux系统支持的所有信号;信号分为可靠信号和不可靠信号,不会丢失支持排队的为可靠信号,不可靠则非之。
2.信号的处理:
第一种:信号的捕捉;调用的API有signal & sigaction,安装信号处理函数function;
格式:signal(int sig, function) ,sig为信号编号,function为信号处理函数,错误发生时返回SIG_ERR.
sigaction(int sig, const struct sigaction &act, struct &oldact);
struct sigaction{
void(*sa_handler) (int); //sa_handler可以为SIG_DFL,SIG_IGN或者信号处理函数名
void(sa_sigaction)(int,siginfo_t ,void*);
sigset_t sa_mask; //信号屏蔽集
int sa_flags; //信号处理的一些相关操作
void(*sa_restorer)(void);//废弃不用
}

#include<stdio.h>
#include<signal.h>
#include<unistd.h>

int tmp =0;

void handle_signal(int number)
{
    printf("receive signal\n");
    sleep(5);
    tmp += 1;
    printf("the value of tmp is:%d\n",tmp);
    printf("in handle_signal,after sleep\n");

}
    int main(int argc,char **argv)
{
    struct sigaction act;
    act.sa_handler = handle_signal;
    act.sa_flags = 0;
    sigemptyset(&act.sa_mask);
    /*安装信号处理函数*/
    sigaction(SIGINT,&act,NULL);
    while(1)
    {
        ;
    }
    return 0;
}
第二种:信号的忽略,对应的信号处理函数为常量SIG_IGN,然而信号编号为SIGKILL  &  SIGSTOP时不能被忽略、捕获、堵塞。
第三种:信号的默认处理。大部分信号的默认操作就是终止进程。

3.信号处理函数的非局部跳转
goto用于函数内部的局部跳转,而处理函数的跳转需要用到setjmp/longjmp or sigsetjmp/siglongjmp 这对组合。
格式:void longjmp(jmp_buf env,int val) / int setjmp(jmp_buf env) ;
void siglongjmp(sigjmp_buf env, int val) / int sigsetjmp(sigjmp_buf env, int savesigs);
二者的相同之处:程序处理时先调用setjmp/sigsetjmp,使用env保存进程的当前的信号屏蔽集,并返回0
二者的区别:信号处理时会自动阻塞正在被处理的信号,将处理的信号加入到屏蔽集中,在信号处理函数返回时把进程的信号屏蔽字恢复,即取消对当前信号的阻塞。前者使用longjmp直接跳转,未让处理函数正常终止,会造成当前信号仍旧是阻塞,无法再次启动信号处理函数,需要手动清除屏蔽信号; 而后者中sigsetjmp多了一个参数savesigs,当savesigs非0时,sigsetjmp中env中保存进程的当前信号的屏蔽集,在调用siglongjmp时会从中恢复保存的信号屏蔽集,从而完成跳传后对处理信号的解除阻塞。

#include<stdio.h>
#include<signal.h>
#include<unistd.h>
#include<setjmp.h>

static sigjmp_buf jmpbuf;

static void handle_signal( int signo)
{
    ptintf("\nSIGINT\n");
    sleep(1);
    siglongjmp(jmpbuf,1);
}

int main( int argc,char **argv)
{
    sleep(5);
    signal(SIGINT,handle_signal);
    if(sigsetjmp(jmpbuf,4) == 1)
    {
        printf("I am jumped\n");
    }
    else
    {
        //raise(SIGQUIT);
        printf("I am waitting signal\n");
        pause();
    }
    return 0;
}

运行结果:I am waitting signal
终端中断
SIGINT
I am jumped
I am here

4.信号的发送
一,int kill(pid_t pid, int signo);执行成功返回值0,失败返回-1,向特定进程发送信号.亦可用命令行 kill -s signo pid
二, int raise(int signo);向调用它的进程发送信号。成功返回0,失败返回非0值;
三, int sigqueue(pid_t pid,int signo, const union sigval alue);向指定进程发送带参数的信号,一般与sigaction(设置对应位)配合使用;执行成功返回值0,失败返回-1;
四,unsigned int alarm(unsigned int seconds),定时器的作用,定时发送SIGALARM信号给调用它的进程。
五,void abort(void),给调用它的进程发送SIGABRT信号
六,int setitimer(int which ,const struct itimerval *value, struct itimerval *ovalue),功能比alarm更全面。
5.信号的屏蔽
信号集的类型:信号总数64个,不能用整型的(通常32位)一位代表一种信号,采用新的数据类型sigset_t;
信号集处理的系统调用: int sigemptyset(sigset_t *set),清空一个信号集,使其不包含任何信号。
int sigaddset(sigset_t *set,int signo),将signo添加到信号集set中。
int sigdelset(sigset_t *set,int signo),将signo从set中删除;成功返回0,失败返回-1,以上类似。
int sigismember(sigset_t *set,int signo),判断信号signo是否在信号集set中,返回1表在,0表示不在,错误返回-1;
信号的屏蔽: 每一个进程都有进程屏蔽集,它规定当前当前阻塞不能递送给进程的信号集;调用系统函数可以改变或者检查进程的信号集。
一,int sigprocmask( int how,sigset_t *set,sigset_t *oldset),其中oldset用于保存以前的屏蔽集,how可取SIG_BLOCK,SIG_UNBLOC,SIG_SETMASK(更改进程的屏蔽集),执行成功返回值0,失败返回-1;
二,int sigpending(sigset_t *set),获取调用进程因被阻塞而不能递送 和 未决排队的信号集,并保存在set中,执行成功返回值0,失败返回-1

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

static void sig_quit(int);

int main(void)
{
    sigset_t newmask, oldmask, pendingmask;

    /* 安装信号处理函数*/
    if (signal(SIGQUIT, sig_quit) == SIG_ERR)
    {
        fprintf(stderr, "can't catch SIGQUIT/n");
        exit(1);
    }
    /* 初始化信号集*/ 
    sigemptyset(&newmask);

    /* 添加指定信号*/*/ 
    sigaddset(&newmask, SIGINT); 

    /*  设置新的屏蔽信号集,并记录旧的屏蔽信号集*/
    if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0) 
    {
        fprintf(stderr, "SIG_BLOCK error/n");
        exit(1);
    }
    sleep(5);

    //获取屏蔽或未决的信号集
    if (sigpending(&pendingmask) < 0)
    {
        fprintf(stderr, "sigpending error/n");
        exit(1);
    }

    //判断信号是否在屏蔽码中
    if (sigismember(&pendingmask, SIGINT))
        printf("/nSIGQUIT pending/n");

    //设置为旧的屏蔽集,即解绑信号,如果有信号,会立即进入信号处理函数中。
    if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
    {
        fprintf(stderr, "SIG_SETMASK error/n");
        exit(1);
    }
    printf("SIGQUIT unblocked/n");
    sleep(5);
    exit(0);
}
static void sig_quit(int signo)
{
    printf("caught SIGQUIT/n");
    //恢复进程对信号的默认处理
    if (signal(SIGINT, SIG_DFL) == SIG_ERR)
    {
        fprintf(stderr, "can't reset SIGQUIT/n");
        exit(0);
    }
}

六,函数sigsuspend 与pause的区别和联系:
函数原型; int sigsuspend( const sigset_t *set),总是返回-1,并将errno置为EINTR
相同点:将进程挂起,直至信号到来;信号处理函数结束后,函数返回并执行下一步操作
区别: sigsuspend = pause + signo(指定的屏蔽信号) ;若set集为空则任何信号都可使信号处理函数作用,否则,屏蔽信号以外的信号才能使起工作。此外,sigsuspend返回后,将进程的信号屏蔽码恢复为以前的屏蔽码

#include <unistd.h>
#include <signal.h>
#include <stdio.h>
void handler(int sig)   //信号处理函数
{
   if(sig == SIGINT)
      printf("\nSIGINT sig\n");

   else if(sig == SIGQUIT)
      printf("\nSIGQUIT sig\n");

   else
      printf("\nSIGUSR1 sig\n");
}
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信号的添加

    sigemptyset(&wait);

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

    sigprocmask(SIG_BLOCK, &new, &old); //设置new为新的信号屏蔽集,并保存旧的屏蔽信号集
    /* 总是返回-1  */
    if(sigsuspend(&wait) != -1)  /*等待SIGUSR1以外的信号到来,执行信号处理函数;若信号之前到来,则执行时会立即
                                     处理被阻塞不能递送和当前未决的信号*/
    printf("\nsigsuspend error\n");
    printf("After sigsuspend");
    sigprocmask(SIG_SETMASK, &old, NULL);/* 恢复进程的屏蔽集*/
    pause();
    printf("for test\n");
    return 0;
}

猜你喜欢

转载自blog.csdn.net/caozhigang129/article/details/78196742