linux 中signal机制如何应用(二)
来源:互联网 发布:联通网络客服人工电话 编辑:程序博客网 时间:2024/05/21 09:33
上一节linux 中signal机制如何应用(一)讲的例子是不带参数的信号处理机制,这一节讲带参数的。
我们知道用kill只是发送信号不能携带参数,如果我们想要发送信号给进程并且携带参数,那就得用sigqueue函数了。可以说,sigqueue函数比kill更加强大,经常是与sigaction()函数配合使用。
sigqueue函数:
int sigqueue(pid_t pid,int signo,const union sigval value); //pid: 指定接收信号的进程pid//signo: 要发送的信号,//sigval: 联合数据结构 sigval,指定了信号传递的参数//返回值: 成功返回0,失败返回-1
union sigval的定义:
typedef union sigval { int sival_int; void * sival_ptr; }sigval_t;
sigqueue()比kill()传递了更多的附加信息,可以向应用程序传递整数或者指向包含更多信息的缓冲区指针。但sigqueue()只能向一个进程发送信号,而不能发送信号给一个进程组。
来思考一个问题,C++的模板函数是怎么实现的?
上一节用sigaction实现的signal函数是不携带参数的,这是原来的函数signal_test(int signo,void (*func)(int))
,第二个参数是函数指针,函数有一个int参数,返回void类型。而现在,我们想要加上携带参数的情形。那么第二个参数的类型就不是固定的,因为需要void (*func)(int signo, siginfo_t *info,void *context)
这种类型的函数指针。我们可能会想到C++的模板函数,但是我们要用C语言实现又该怎么办呢?办法还是有的,这时可以把参数定义为void*类型,参数传进函数后,在函数体里面强制转化为函数指针就可以了。
我们在下面定义了两个函数指针,用于在信号处理函数中强制转化指针类型。具体采用哪一个需要根据flag类型来确定。
typedef void (*pFunc)(int);typedef void (*pSignalFunParam)(int, siginfo_t *, void *);
通常情况按照下列方式调用信号处理函数:
void handler(int signo);
需要填充sigaction结构体成员act.sa_handler = handler;
但是,如果设置了SA_SIGINFO标志,也就是act.sa_flags = SA_SIGINFO时,要按照下列方式填充sigaction结构体成员和调用信号处理程序:
void handler(int signo, siginfo_t *info,void *context);
需要填充sigaction结构体成员act.sa_sigaction = handler;
这两种调用方式在接下来的程序中都会实践。这两种中断处理函数区别在于,一个是不携带参数,一个携带参数,并且保存在siginfo_t结构体中,信号处理函数可以解析这些数据。
接下来用一个例子来讲带参数的信号处理机制怎么实现。
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <errno.h>#include <sys/types.h>#include <sys/wait.h>#include <signal.h>int logFlag = 0;//全局变量,是否打印log的标志//信号没有带参数时使用的中断处理函数void signal_handler(int signo){ switch(signo) { case SIGINT: //SIGINT默认行为是退出进程 printf("in signal_handler() SIGINT signal\n"); exit(0); break; case SIGALRM: //SIGALRM默认行为是退出进程 printf("SIGALRM signal\n"); break; } }//信号携带参数时使用的中断处理函数void signal_handler_param(int signo,siginfo_t *info,void *context){ switch(signo) { case SIGINT: //SIGINT默认行为是退出进程 printf(" SIGINT signal \n"); exit(0); break; case SIGUSR1: //当logFlag等于1的时候表示打开log标志 logFlag = 1; printf("SIGUSR1 , signo=%d ,logFlag = %d,recv data :%d\n",signo,logFlag,info->si_value.sival_int); break; case SIGUSR2: printf("SIGUSR2 ,signo=%d , recv data :%d\n",signo,info->si_value.sival_int); break; } return ;}typedef void (*pFunc)(int);typedef void (*pSignalFunParam)(int, siginfo_t *, void *);int signal_sigaction(int signo,void *func,int flag){ struct sigaction act,oact; //填充软中断函数 switch(flag) { case 0: act.sa_handler = (pFunc)func; break; case SA_SIGINFO: act.sa_sigaction = (pSignalFunParam)func; break; } //act.sa_handler=func; //将act的属性sa_mask设置一个初始值 sigemptyset(&act.sa_mask); act.sa_flags=flag; int ret = sigaction(signo,&act,&oact); if (ret !=0 ) { printf("sigaction() failed !\n"); } return ret;}int main(int arg, char *args[]){ pid_t pid=0; pid=fork(); if(pid==-1) { printf("fork process failed!\n"); return -1; } if(pid>0)//父进程 { printf("in father!\n"); //linux内核为用户程序保留了两个信号,一个是10 SIGUSR1还有12 SIGUSR2 //要想接受信号带来的参数,必须使用了SA_SIGINFO选项 signal_sigaction(SIGUSR1,signal_handler_param,SA_SIGINFO); signal_sigaction(SIGUSR2,signal_handler_param,SA_SIGINFO); //不带参数 signal_sigaction(SIGINT,signal_handler,0); //等待子进程发送信号 while(1) { //pause()会令目前的进程暂停(进入睡眠状态), 直到被信号(signal)所中断 pause(); } printf("recv signal from child\n"); } if(pid==0)//子进程 { printf("in child!\n"); //因为我们不知道子进程还是父进程先执行,这里等待是为了父进程执行完信号安装 sleep(2); //向父进程发送带数据的信号 union sigval sigvalue; sigvalue.sival_int=110; //发送信号SIGUSR1 if(sigqueue(getppid(),SIGUSR1,sigvalue)==-1) { printf("sigqueue() failed!\n"); exit(0); } //发送信号SIGUSR2 if(sigqueue(getppid(),SIGUSR2,sigvalue)==-1) { printf("sigqueue() failed!\n"); exit(0); } printf("child process send signal successfully\n"); exit(0); } return 0;}
编译:
ubuntu:~/test/signal_test$ gcc 2signal.c -o 2signal
后台运行:
ubuntu:~/test/signal_test$ ./2signal &
实验结果:
[1] 46814ubuntu:~/test/signal_test$ in father!in child!child process send signal successfullySIGUSR2 ,signo=12 , recv data :110SIGUSR1 , signo=10 ,logFlag = 1,recv data :110
kill发送SIGINT信号:
ubuntu:~/test/signal_test$ kill -INT 46814in signal_handler() SIGINT signal
从上面的程序和实验结果理解程序的流程,先调用fork函数,创建子进程,子进程先睡眠两秒等待父进程完成信号的安装,然后父进程调用pause函数,令目前的进程暂停(进入睡眠状态), 直到被信号(signal)所中断,接着子进程向父进程发送SIGUSR1,SIGUSR2的信号,触发了中断,进程根据信号的类型调用了不同的中断处理函数。当发送的是SIGUSR1和SIGUSR2信号,会调用signal_handler_param函数,当发送的是SIGINT信号,则调用的是signal_handler。
- linux 中signal机制如何应用(二)
- linux 中signal机制如何应用(一)
- Linux中signal机制阐述
- Linux中signal机制阐述
- linux信号signal处理机制(二)
- Linux中如何实现Signal?
- linux中inotify机制如何应用
- linux信号机制signal
- linux signal 处理机制
- Linux Signal 信号机制
- Linux 下 signal 机制
- linux如何实现signal?
- Linux信号signal处理机制
- Linux信号signal处理机制
- Linux 信号signal处理机制
- Linux信号signal处理机制
- Linux 信号signal处理机制
- Linux 信号signal处理机制
- Ajax简单实现文件异步上传的多种方法
- 朴素的矩阵行列式计算之C++实现
- TCP/IP参考模型
- Android 复制剪切板
- Mac系统git的配置和使用注意事项
- linux 中signal机制如何应用(二)
- docker部署node及mongodb,node.js读取mongodb数据以Web显示给用户
- Web攻击原理及其防护
- floateActionButton的一些使用情况
- GridControl应用点滴之ReadOnly Vs AllowEdit
- php实现无限极分类
- Eureka-服务注册于发现
- Line: 209
- No12&13 Integer and Roman