1、signal - 捕获信号
signal 函数的使用方法简单,但并不属于 POSIX 标准,在各类 UNIX 平台上的实现不尽相同,因此其用途受到了一定的限制。
功能 | 简单的信号处理函数(信号捕捉函数) |
头文件 | #include <signal.h> |
原型 | typedef void (*sighandler_t)(int); //信号处理函数的 指针
sighandler_t signal(int signum, sighandler_t handler); |
参数 | signum:要捕捉的信号 handler:处理方式 1、SIG_IGN:忽略这个信号
|
返回值 | 成功:返回信号处理程序的先前值 出错:返回SIG_ERR,并设置errno |
备注 | 详细请查看man手册:man 2 signal (第7本也有)。可以配合kill()函数使用,kill()函数向某个进程发送某个指定的信号。 |
示例:
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void SIGINT_handle(int sig_num)
{
printf("reveice signal %d \n", sig_num);
}
int main(int argc, char argv[])
{
int i;
signal(SIGINT, SIGINT_handle);
for(i=0; i<10; i++)
{
printf("i=%d \n", i);
sleep(1);
}
return 0;
}
2、kill - 发送信号
功能 | 向一个进程发送信号 |
头文件 | #include <sys/types.h> #include <signal.h> |
原型 | int kill(pid_t pid, int sig); |
参数 | (1)pid:目标进程ID (process id) pid>0给指定进程 发送sig信号,pid=0给同组进程 发送sig信号,pid<-1给指定进程组 发送sig信号,pid=-1给所有进程 发送sig信号。 (2)sig:要发送的信号 |
返回值 | 成功:返回0 失败:返回-1,并设置error。 |
备注 | 详细请查看man手册:man 2 kill |
示例:
发送方 kill.c
//发送方 kill.c
#include <stdio.h>
#include <sys/types.h>
#include <signal.h>
int main(int argc, char *argv[])
{
int pid, ret;
if(argc <2)
{
printf("arg error![./kill pid]\n");
return -1;
}
sscanf( argv[1], "%d", &pid);
ret = kill(pid, SIGUSR1);
if(ret != 0 )
{
perror("kill SIGUSR1 error");
return -1;
}
printf("send SIGUSR1 to [pid]%d successful!\n", pid);
return 0;
}
接收方 signal.c
#include <stdio.h>
#include <unistd.h> //sleep()
#include <signal.h>
#include <stdlib.h> //exit()
void SIGUSR1_handle(int sig_num)
{
printf("reveice signal %d \n", sig_num);
exit(0);
}
int main(int argc, char argv[])
{
int i;
signal(SIGUSR1, SIGUSR1_handle);
for(i=0; i<30; i++)
{
printf("i=%d \n", i);
sleep(1);
}
return 0;
}
3、sigprocmask - 阻塞信号
在阻塞过程中,信号不会丢失,会被挂起,等解开阻塞的时候再响应。意思就是在接收到信号的时候,对应的处理函数会暂停执行,当阻塞停止时,触发几次信号不会执行几次对应的处理函数,具体执行几次跟处理函数是否在执行有关!!这个切记。
功能 | 阻塞信号,延迟响应 。(检查并更改阻塞的信号) |
头文件 | #include <signal.h> |
原型 | int sigprocmask(int how, const sigset_t *set, sigset_t *oldset); |
参数 | how:对信号的操作(应用于信号集合中的所有函数) SIG_BLOCK:添加信号集合里面的信号进行阻塞(原本的设置上添加设置) set:要设置的信号集合 注意:sigaddset()、sigdelset()、sigismember()等函数调用之前必须先调用sigemptyset()或者sigfillset()函数初始化信号集。 oldset:用来保存旧的信号集合设置 |
返回值 | 成功:返回0 失败:返回-1,并设置error |
备注 | 在阻塞状态时,非实时信号可能会丢失 |
例子:对中断信号、退出信号进行阻塞。然后向sigprocmask程序发送中断信号(Ctrl+c)、退出信号(Ctrl+\)。那么sigprocmask程序收到信号后,不会立即响应,直到解除阻塞,才进行响应。其中,发送了两次中断信号(姑且命名为中断A,中断B),但是只响应了一次,是因为: 在某个时刻或阻塞时 接收到多个同一种非实时信号时,只会响应一次,其余丢弃,如果有一个已经开始响应了但是还没有结束,然后又来一个,那么这一个也会响应。
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void signal_handle(int sig_num)
{
printf("receive signal=%d\n", sig_num);
}
int main(void)
{
int i;
sigset_t sigset;
signal(SIGINT, signal_handle);
signal(SIGQUIT, signal_handle);
sigemptyset(&sigset);//清空集合
sigaddset(&sigset, SIGINT);//将中断信号添加到 阻塞集合中
sigaddset(&sigset, SIGQUIT);//将退出信号添加到 阻塞集合中
sigprocmask(SIG_BLOCK, &sigset, NULL);//对信号进行阻塞
for(i=0; i<10; i++)
{
printf("i=%d \n", i);
sleep(1);
}
sigprocmask(SIG_UNBLOCK, &sigset, NULL);//解开对信号的阻塞
printf("hello\n");
return 0;
}
运行结果:
4、sigaction - 带数据的信号处理
POSIX 标准定义的信号处理接口是 sigaction 函数
功能 | 检查并改变信号动作(捕捉信号,指定处理动作) |
头文件 | #include <signal.h> |
原型 | int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact); |
参数 | (1)signum:捕捉的信号 (2)act:信号动作结构体,用来登记对这个信号进行处理的动作。 struct sigaction (3)oldact:用来保存原来的设置,如果是NULL则不保存。 |
返回值 | 成功:返回0 失败:返回-1,并设置error |
备注 | siginfo_t 参数是一个如下定义的结构体:从中可以获取信号编号,进程ID等等信息 siginfo_t { int si_signo; /*信号编号*/ int si_errno; /*错误号*/ int si_code; /*信号码*/ int si_trapno;/*导致硬件产生信号的trap号*/ pid_t si_pid; /*发送进程ID*/ uid_t si_uid; /*发送进程真实的用户ID*/ int si_status; /*退出值*/ clock_t si_utime; /*花费的用户时间*/ clock_t si_stime; /*花费的系统时间*/ sigval_t si_value; /*信号值*/ int si_int ; /*POSIX.1b 信号*/ void* si_ptr; /*POSIX.1b 信号*/ int si_overrun int si_timerid void* si_addr long si_band int si_fd; short si_addr_lsb; void* si_lower; /* Lower bound when address violation occurred (since Linux 3.19) */ }
当 sa_flags 成员的值包含了 SA_SIGINFO 标志时,系统将使用 sa_sigaction 函数作为信号处理函数,否则使用 sa_handler 作为信号处理函数。 sa_flags 成员用于指定信号处理的行为,它可以是一下值的“按位或”组合。
|
5、sigqueue - 带数据的信号发送
功能 | 将信号和数据发送到指定进程 |
头文件 | #include <signal.h> |
原型 | int sigqueue(pid_t pid, int sig, const union sigval value); |
参数 | (1)pid:目标进程ID (2)sig:要发送的信号 (3)value:附加数据 union sigval //注:这是共用体、两者取其一 |
返回值 | 成功:返回0 失败:返回-1,并设置error |
备注 | 跟sigaction结合使用 |
例子:sigqueue程序向sigaction程序发送SIGUSR1信号,并附带int型数据123,sigaction捕捉到信号后,打印出信号发送方的PID和附加的数据,然后退出。
接收方 sigaction.c
include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
void sigaction_handle(int signum, siginfo_t *info, void * ucontext)
{
printf("info.si_pid=%d\n", info->si_pid);//打印信号发送方的pid
printf("info.si_int=%d\n", info->si_int);//打印信号发送方的附加数据
exit(0);
}
int main(void)
{
int i;
struct sigaction act, oldact;
act.sa_flags = SA_SIGINFO;//使用带参数的信号处理函数
act.sa_sigaction = sigaction_handle;
//sigemptyset(&act.sa_mask);//清空原来集合
//sigfillset(&act.sa_mask);//将所有信号添加到集合
sigaction(SIGUSR1, &act, &oldact);//捕捉sigqueue发送的SIGUSR1信号
for(i=0; i<30; i++)
{
printf("i=%d\n", i);
sleep(1);
}
return 0;
}
发送方 sigqueue.c
#include <stdio.h>
#include <signal.h>
#include <sys/types.h>//getpid()
#include <unistd.h>
int main(int argc, const char *argv[])
{
if(argc < 2)
{
printf("arg error\n");
return -1;
}
union sigval value;
value.sival_int = 123;
int sig_num, pid;
sscanf(argv[1], "%d", &pid);
sigqueue(pid, SIGUSR1, value);//向指定的pid发送SIGUSR1信号
printf("mypid=%d\n", getpid());//打印当前进程的ID号
return 0;
}
结果
6、注意事项
异步信号包含: 非实时信号 、实时信号。
实时信号一定会响应,非实时信息号不一定会响应(可能会被忽略,或丢失)。信号一般有以下设定:
1,捕捉 (收到某个信号,做指定的动作,而不是做默认的)
2,忽略 (收到某个信号,不做什么动作)
3,阻塞 (收到某个信号,先做完当前事情,然后在响应信号)
4,按照默认动作
注:SIGKILL,SIGSTOP不能被捕捉
其中SIGKILL,SIGSTOP只能按照系统默认动作执行,不能捕捉,阻塞及忽略
SIGPIPE(13) 管道破损,没有读端的管道写数据,就是那个brokenpipe。默认是杀进程的,所以网络编程中要处理这个信号。
前面31个是非实时信号:
1,每一个非实时信号对应一个信号名
2,每一个非实时信号基本上都会对应一个系统事件
3,信号有可能会丢失
后面的31个是实时信号:
1,信号不会丢失
2,优先级比非实时信号要高
7、其他产生信号的函数
raise(sig) 函数:给当前进程发送sig信号。
abort() 函数:给当前进程发送SIGABRT信号。
alarm(sec) 函数:sec大于0时表示sec秒后给当前进程发送SIGALRM信号。sec=0表示取消定时器。每个进程只有唯一一个定时器。
setitimer() 函数:周期定时器,which可选:ITIMER_REAL(自然定时,产生SIGALRM信号)、ITIMER_VIRTUAL(虚拟用户空间计时,产生SIGVTALRM信号)、ITIMER_PROF(运行时计时,产生SIGPROF信号)