信号处理函数创建signal、sigaction、kill、sigqueue


前言

信号是事件发生时对进程的通知机制。也称之为软件中断
信号分两类:
①、用于内核向进程通知事件,构成标准信号。编号范围1~31。不支持排队处理。可供使用:SIGUSR1和SIGUSR2。不可靠信号。
②、由实时信号构成,编号范围32~63.采用SIGRTMIN+x 形式.支持排队(队列化管理)。信号编号越小,优先级越高,同级看接收时间先后。可靠信号。可携带数据信息

信号的处理:忽略、捕捉(设置处理程序)、默认行为
忽略:内核将信号丢弃,信号对进程没有产生影响,但是有两种信号不能被忽略(分别是 SIGKILL和SIGSTOP)
捕捉(设置处理程序):设置一个某待捕捉的信号处理函数,当接受到该信号时,就会去处理对应的函数。
默认行为:每个信号来说,系统都对应由默认的处理动作,当发生了该信号,系统会自动执行。不过,对系统来说,大部分的处理方式都比较粗暴,就是直接杀死该进程。
在这里插入图片描述


信号处理函数的注册
①、signal——原始版
②、sigaction——首选版 //可收到数据信息
信号发送
①、kill——原始版
②、sigqueue——首选版 //可发送信息

一、信号处理函数signal()

1、信号处理函数注册signal()

void (*signal(int sig, void (*func)(int)))(int);
等效于:  传入一个函数指针,返回一个函数指针。
typedef void (*sighandler_t)(int);//参数int是传入信号的编号,方便一个信号处理函数对应多个信号

sighandler_t signal(int sig, sighandler_t handler);
成功,返回之前的处置  失败:SIG_ERR
handler:信号处理函数
		或者是SIG_IGN:忽略这个信号 SIG_DFL:恢复默认

signum:哪个信号

2、信号发送kill()

int kill(pid_t pid, int sig);
返回0成功
pid:进程id
sig:要发送的信号

pid > 0:将发送个该 pid 的进程
pid == 0:将会把信号发送给与发送进程属于同一进程组的所有进程,并且发送进程具有权限想这些进程发送信号。
pid < 0:将信号发送给进程组ID 为 pid 的绝对值得,并且发送进程具有权限向其发送信号的所有进程
pid == -1:将该信号发送给发送进程的有权限向他发送信号的所有进程。(不包括系统进程集中的进程)

3、练习demo.c

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

static volatile int flag = 0;
//typedef void (*sighandler_t)(int);
void handler(int signum)
{
	if(signum == SIGINT|| signum == SIGTERM)
	{
		flag = 1;
		return;
	}
    if(signum == SIGIO)
        printf("SIGIO   signal: %d\n", signum);
    else if(signum == SIGUSR1)
        printf("SIGUSR1   signal: %d\n", signum);
    else
        printf("error\n");
}

int main(void)
{
    //sighandler_t signal(int signum, sighandler_t handler);
    signal(SIGIO, handler);
    signal(SIGUSR1, handler);
    printf("set handler signal:%d  %d\n", SIGIO, SIGUSR1);
    
    printf("receive a signal before\r\n"); 
    
    while(!flag)
    {
	    pause(); /*程序开始执行,就象进入了死循环一样,这是因为进程正在等待信号,当我们
	    		按下Ctrl-C时(获接受到一个信号),信号被捕捉,并且使得pause退出等待状态。*/
	    printf("receive a signal \r\n"); 
    }
    return 0;
}

4、阻塞信号(信号掩码)sigprocmask()与等待状态信号sigpending()

阻塞信号:信号发出后,并不能直接被接收到(传递延后),处于阻塞状态,只能等解除阻塞或者设为忽略
等待状态信号:被设为阻塞的信号,被发出后就是处于等待状态信号

阻塞的设置和等待状态信号是通过状态字来记录的。分别为sigprocmask()和sigpending()

①、阻塞状态字获取和设置

int sigprocmask(int how, const sigset_t * set,
							sigset_t * oldset);
success: 0  error: -1
how:
	SIG_BLOCK:将set信号集添加到信号掩码里(并集 |)
	SIG_UNBLOCK:将set信号集从信号掩码里移除(&)
	SIG_SETMASK:将set信号集赋给信号掩码(=)
set:想要操作的信号集的数据对象
oldset:之前的信号集数据对象   如不需要可设为NULL

只可读取目前处于等待状态的信号
int sigpending(sigset_t *set);
success: 0  error: -1
set:存放返回的状态

该函数管理信号,是通过信号集的数据结构来进行管理的,信号集可以通过以下的函数进行管理。

②、信号集操作函数(状态字表示)

int sigemptyset(sigset_t *set);  //初始化 set 中传入的信号集,清空其中所有信号
int sigfillset(sigset_t *set);  //把信号集填1,让 set 包含所有的信号
int sigaddset(sigset_t *set, int signum);//把信号集对应位置为1
int sigdelset(sigset_t *set, int signum);//吧信号集对应位置为0
int sigismember(const sigset_t *set, int signum);//判断signal是否在信号集

设置/解除信号阻塞的步骤:

a、分配内存空间sigset sigset bset;
b、置空sigemptyset(&bset);
c、添加信号****sigaddset(&bset, SIGINT);
d、添加其他需要管理的信号…
e、设置信号集中的信号处理方案(此处为解除阻塞)sigprocmask(SIG_UNBLOCK, &bset, NULL);

5、等待信号pause()、raise()、killpg()

将暂停进程的执行,直到信号处理器函数中断该调用为止

int pause(void);

进程向自身发送信号

int raise(int sig);

向某一进程组的所以成员发送一个信号

int killpg(pid_t pgrp,int sig);

二、信号处理函数sigaction() 首选

sigaction是设置信号的另一种方法,与signal区别
①、允许获得信号时无需将其改变
②、可携带额外信息数据(主要)

实时信号:SIGRTMIN~SIGRTMAX之间的信号,表示法:SIGRTMIN+x
实时信号可支持排队。设阻塞的信号,多次发该信号,并不是最终只发一次,而是将发出的信号排队处理,即能接受多个同信号

1、信号处理函数注册sigaction()

int sigaction(int sig, const struct sigaction* act,
      					 struct sigaction* oldact);
 sucess: 0    error:-1   
sig:获取或改变的信号编码(除SIGKILL和SIGSTOP)
act:信号新处置的对象,如仅对旧信号感兴趣可设为NULL
oldact:之前的信号处置,不意可NULL


struct sigaction {
union{
void       (*sa_handler)(int); //信号处理程序,不接受额外数据,SIG_IGN 为忽略,SIG_DFL 为默认动作
void       (*sa_sigaction)(int, siginfo_t *, void *); //信号处理程序,能够接受额外数据和sigqueue配合使用
};
sigset_t   sa_mask;/*阻塞关键字的信号集,可以在调用捕捉函数之前,把信号(包括自身
信号)添加到信号阻塞字,信号捕捉函数返回之前恢复为原先的值。防止在执行处理函数时被自己后指定信号打断。*/

int        sa_flags;//影响信号的行为SA_SIGINFO表示能够接受数据
void (*sa_restorer)(void);//仅供内部使用
 };
回调函数句柄sa_handler、sa_sigaction只能任选其一
sa_mask:设置执行处理函数时不可被中断的信号(包括自身);  
sa_flags:处理过程选项: SA_SIGINFO(携带额外信息)  SA_RESTART(自动重启中断的系统调用)				 

在处理函数的struct siginfo这个结构体主要适用于记录接收信号的一些相关信息。
sigval si_value这个成员中有保存了发送过来的信息;同时,在si_int或者si_ptr成员中也保存了对应的数据。

 siginfo_t {
    int      si_signo;    /* Signal number */
    int      si_errno;    /* An errno value */
    int      si_code;     /* Signal code */
    int      si_trapno;   /* Trap number that caused
                             hardware-generated signal                   
                            (unused on most architectures) */                     
    pid_t    si_pid;      /* Sending process ID */
    uid_t    si_uid;      /* Real user ID of sending process */
    int      si_status;   /* Exit value or signal */
    clock_t  si_utime;    /* User time consumed */
    clock_t  si_stime;    /* System time consumed */
    union    sigval si_value;    /* Signal value data from sigqueue */
    int      si_int;      /* POSIX.1b signal */
    void    *si_ptr;      /* POSIX.1b signal */
    int      si_overrun;  /* Timer overrun count; POSIX.1b timers */
    int      si_timerid;  /* Timer ID; POSIX.1b timers */
    void    *si_addr;     /* Memory location which caused fault */
    int      si_band;     /* Band event */
    int      si_fd;       /* File descriptor */
}

union sigval {
   int   sival_int;
   void *sival_ptr;
 };

2、信号发送sigqueue()

使用前提:
①、制定了 SA_SIGINFO 的标志
②、sa_sigaction 成员提供了信号捕捉函数

int sigqueue(pid_t pid, int sig, const union sigval value);
pid:进程id
sig:信号编号
value:存放额外携带信息

union sigval {
   int   sival_int;
   void *sival_ptr;
 };

发送端:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>

#define MYSIG SIGRTMIN+5

int main(int argc,char *argv[])
{
	int signo=MYSIG; 
	union sigval mysigval; 
	int i;	
	pid_t pid;

	pid=(pid_t)atoi(argv[1]);
   
	for(i=0;i<10;i++)
	{
		mysigval.sival_int=i;  
		//向指定进程发送一个信号和数据
		printf("sigqueue signo=%d, mysigval.sival_int=%d\n",signo, mysigval.sival_int);  
		if(sigqueue(pid, signo, mysigval)==-1)	
		{  
			printf("sigqueue fail\n");	
			return -1;	
		}	
		sleep(1);
	}	
	return 0;
}



3、练习demo.c

接受端

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#define MYSIG SIGRTMIN+5
int flag = 1;

//信号响应函数 
void new_act(int signum, siginfo_t * info, void * myact)  
{  
    printf("new_act=%d\n", signum);  
    printf("new_act->%d,%d\n", info->si_int,info->si_value.sival_int);  /*从发送端传过来的数据,两个值相同*/
    if(info->si_int == 9)
    {
        flag = 0;
    }
}  

int main(int argc,char *argv[])
{
    int signo=MYSIG;  
    pid_t pid;
    struct sigaction myAct;       //指明对信号响应的设置信息
    
    pid = getpid();// 调用getpid函数的进程是什么,就返回该进程的ID
    printf("process id is %d,%d\r\n",pid, signo);
/*
struct sigaction
{ 
    void (*sa_handler)(int); 
    void (*sa_sigaction)(int, siginfo_t *, void *); 
    sigset_t sa_mask; 
    int  sa_flags; 
    void(*sa_restorer)(void); 
};
*/
    //标识指明,信号响应函数可以使用传输过来的参数  
    //使用sa_sigaction成员而不是sa_handler作为信号处理函数。
    myAct.sa_flags=SA_SIGINFO;  
    //指明信号响应函数   
    myAct.sa_sigaction=new_act;  
    //安装信号,为MYSIG设置响应函数 
    if(sigaction(signo, &myAct, NULL)<0)  
    {  
        printf("sigaction fail\n");  
    }  
  
    while(flag)  
    {  
        sleep(2);  
        printf("sleep \n");  
    }  
    return 0;
}

在这里插入图片描述


三、扩展定时SIGALRM

定时器产生的SIGALRM

函数原型:int setitimer(int which, const struct itimerval *new_value,  
            struct itimerval *old_value);  
函数描述:计时器功能。
参数说明:
Which:表示类型,可选的值有:
ITIMER_REAL:以系统真实的时间来计算,它送出SIGALRM信号。
ITIMER_VIRTUAL:以该进程在用户态下花费的时间来计算,它送出SIGVTALRM信号。
ITIMER_PROF:以该进程在用户态和内核态下所费的时间来计算,它送出SIGPROF信号。

new_value:用来对计时器进行设置
old_value:通常用不上,设置为NULL,它是用来存储上一次setitimer调用时设置的new_value值。


struct itimerval {  
    struct timeval it_interval; /* next value 第一次以后发出信号的时间间隔*/  
    struct timeval it_value;    /* current value 第一次发出信号的时间间隔*/  
};  
  
struct timeval {  
    time_t      tv_sec;         /* seconds */  
    suseconds_t tv_usec;        /* microseconds */  
};  
itimeval又是由两个timeval结构体组成,timeval包含tv_sec和tv_usec两部分,其中tv_se为秒,tv_usec为微秒(1/1000000)。
settimer工作机制是,先对it_value倒计时,当it_value为零时触发信号,然后重置为it_interval,继续对it_value倒计时,一直这样循环下去。
基于此机制,setitimer既可以用来延时执行,也可定时执行。
            

参考与引用:https://www.jianshu.com/p/f445bfeea40a

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值