信号处理函数sigaction()

sigaction():信号处理函数 可以携带信息~

头文件 #include <signal.h>

int sigaction(int signum,const struct sigaction *act, struct sigaction *oldact));

一个参数为信号的值,可以为除SIGKILLSIGSTOP外的任何一个特定有效的信号(为这两个信号定义自己的处理函数,将导致信号安装错误)。

第二个参数是指向结构sigaction的一个实例的指针,在结构sigaction的实例中,指定了对特定信号的处理可以为空,进程会以缺省方式对信号处理

第三个参数oldact指向的对象用来保存原来对相应信号的处理,可指定oldactNULL

如果把第二、第三个参数都设为NULL,那么该函数可用于检查信号的有效性。

struct sigaction
{
    union
    {
        __sighandler_t _sa_handler;
        void (*_sa_sigaction)(int, struct siginfo *, void *);
    } _u
    sigset_t sa_mask;
    unsigned long sa_flags;
    void (*sa_restorer)(void);
}
 

1联合数据结构中union的两个元素_sa_handler以及*_sa_sigaction指定信号关联函数

_sa_handler指定的处理函数只有一个参数,即信号值,所以信号不能传递除信号值之外的任何信息;

_sa_sigaction是指定的信号处理函数带有三个参数,是为实时信号而设的(当然同样支持非实时信号),它指定一个3参数信号处理函数。第一个参数为信号值,第三个参数没有使用(posix没有规范使用该参数的标准),第二个参数是指向siginfo_t结构的指针,结构中包含信号携带的数据值

typedef struct
{
    int si_signo;  /*信号值,对所有信号有意义*/
    int si_errno;  /*errno值,对所有信号有意义*/
    int si_code;   /*信号产生的原因,对所有信号有意义*/
    union
    {
        sigval si_value;
    } 
}siginfo_t;
//结构的第四个域同样为一个联合数据结构:
union sigval
{
    int si_int;
    void *si_ptr;
}

深深的不能理解为什么结构体中的第四个变量又多次一举声明为sigval?

像下面这样不是更好?是不是因为处理的信号不同,结构体里的值也会发生变化?

// 自己理解

typedef struct
{
    int si_signo;  /*信号值,对所有信号有意义*/
    int si_errno;  /*errno值,对所有信号有意义*/
    int si_code;   /*信号产生的原因,对所有信号有意义*/
    union
    {
        int si_int;
        void *si_ptr;
    } 
}siginfo_t;// 

采用联合数据结构,说明 siginfo_t 结构中的 si_value 要么持有一个 4 字节的整数值,要么持有一个指针,这就构成了与信号相关的数据。

接受信号例子:

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
void new_op(int,siginfo_t*, void*);
int main(int argc,char **argv)
{
	struct sigaction act;// 创建sigaction结构体
	int sig;
	pid_t pid;              
        
	pid=getpid();
        printf("PID is %d\n", (int)pid);
	sig=atoi(argv[1]);  //输入时第一个参数 信号的值        (kill -l) 可以查看所有信号

            
	sigemptyset(&act.sa_mask);
	act.sa_sigaction=new_op;//处理函数
	act.sa_flags=SA_SIGINFO;//标志

	if(sigaction(sig,&act,NULL)<0)
	{
		printf("install sigal error\n");
		exit(0);
	}
	while(1)
	{
		sleep(2);
		printf("wait for the signal\n");
	}
}
void new_op(int signum,siginfo_t *info,void *myact)
{
	printf("the int value is %d \n",info->si_int);
}

sa_sigaction函数指针指向了处理函数new_op,在接收到规定信号,执行相应的new_op 操作,并将获得的值(info->si_int)显示出来

也就是说在编程中,对于sigaction()特定信号的处理,只需要将指针指向sa_sigaction 指向处理函数就行,剩下数据的接受可在处理函数参数  siginfo_t 中直接操作

此时的new_op 更像一个中断函数

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

@ubuntu:~/Desktop$ ./sig1 50 //输入参数


PID is 3344      // 结果显示
wait for the signal
wait for the signal
wait for the signal
wait for the signal
the int value is 8
wait for the signal
wait for the signal
wait for the signal

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

发送信号例子:

系统调用sigqueue发送信号时,sigqueue的第三个参数就是sigval联合数据结构,当调用sigqueue时,该数据结构中的数据就将拷贝到信号处理函数的第二个参数中。

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/time.h>
#include <unistd.h>
#include <sys/types.h>
// sigqueue 发送信号信息,带sival_int的信息
main(int argc,char **argv)
{
	pid_t pid;
	int signum;
	union sigval mysigval;
	signum=atoi(argv[1]);    //要发送信号的信号值
	pid=(pid_t)atoi(argv[2]);//发送进程的pid
	mysigval.sival_int=8;//不代表具体含义,只用于说明问题
	if(sigqueue(pid, signum, mysigval)==-1)
	{
		printf("send error\n");
		exit(0);
	}
	sleep(2);
}
@ubuntu:~/Desktop$ ./sig2 50 3344 // 输入参数


程序运行次序 先运行sig1 使其不断的等待接收 再运行sig2 并在后面加入信号值为50 进程号(pid)为3344参数

结果解释:当sig1 不断等待时,如果sig2发送的信号到达,就显示the int value is 8 表示已经接收到信号


二。 signal()


#include <signal.h>
void (*signal( int signum, void(*handler) (int) ))(int);

第一个参数指定信号的值,第二个参数指定针对前面信号值的处理

如果signal()调用成功,返回最后一次为安装信号signum而调用signal()时的handler值;失败则返回SIG_ERR

Linux中signal 和signalaction 的区别就在是否能携带信号信息,在Uinx中貌似还有区别


三.实时信号 非实时信号


// 这个例子主要是对于实时信号和非实时信号阻塞的验证
// 利用另一个终端连续发送 kill -SIGRTMIN 进程号 可验证实时信号是可靠的,不会丢失
// 利用在本终端连续发送ctrl+c可发现最终是受到一个信号! 即信号不可靠
//方法:先将两个信号屏蔽,并sleep一会儿 ,这期间连续发送信号~
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>

void sig_handler(int,siginfo_t*,void*);

int main(int argc,char**argv)
{
    struct sigaction act;
    sigset_t newmask, oldmask;
    int rc; 
    sigemptyset(&newmask);  
    sigaddset(&newmask, SIGINT); //非实时信号 
    sigaddset(&newmask, SIGRTMIN); //实时信号
    sigprocmask(SIG_BLOCK, &newmask, &oldmask); //屏蔽信号,使其不会立即执行信号的动作
    act.sa_sigaction = sig_handler;
    act.sa_flags = SA_SIGINFO;
 
    if(sigaction(SIGINT, &act, NULL) < 0)
    {
        printf("install sigal error\n");
    }
 
    if(sigaction(SIGRTMIN, &act, NULL) < 0)
    {
        printf("install sigal error\n");
    }
 
    printf("pid = %d\n", getpid()); 
    sleep(20); // 休眠中 ,发送过来的信号会排队
    sigprocmask(SIG_SETMASK, &oldmask, NULL); //打开屏蔽,信号执行相应的操作
    return 0;
}
void sig_handler(int signum,siginfo_t *info,void *myact)
{
    if(signum == SIGINT)
        printf("Got a common signal\n");
    else
        printf("Got a real time signal\n");
}
 

非实时信号都不支持排队,都是不可靠信号;实时信号都支持排队,都是可靠信号。


四。信号集合

信号集的数据类型

typedef struct {
         unsigned long sig[_NSIG_WORDS];
} sigset_t

信号集用来描述信号的集合,linux所支持的所有信号可以全部或部分的出现在信号集中,主要与信号阻塞相关函数配合使用。下面是为信号集操作定义的相关函数:

#include <signal.h>
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);
sigemptyset(sigset_t *set)初始化由set指定的信号集,信号集里面的所有信号被清空;
sigfillset(sigset_t *set)调用该函数后,set指向的信号集中将包含linux支持的64种信号;
sigaddset(sigset_t *set, int signum)在set指向的信号集中加入signum信号;
sigdelset(sigset_t *set, int signum)在set指向的信号集中删除signum信号;
sigismember(const sigset_t *set, int signum)判定信号signum是否在set指向的信号集中。











你可能感兴趣的:(linux,signaltion)