概述
什么是信号:
信号是在软件层次上对中断机制的模拟(软中断),是一种异步通信方式。
进程对信号的响应方式:
- 缺省方式:根据默认行为响应信号
- 忽略信号:不响应信号
- 捕捉信号:根据指定行为响应信号
常用信号及含义:
见博文"4.Linux_Shell命令"-"进程管理"-"2、向进程发送信号",博文连接如下:
4.Linux_Shell命令-CSDN博客
信号相关命令:
见博文"4.Linux_Shell命令"-"进程管理"-"2、向进程发送信号",博文连接如下:
4.Linux_Shell命令-CSDN博客
信号的状态:
信号递达:代表信号已经被进程接收到,不论进程的处理方式是缺省、忽略还是捕捉。
信号未决:代表信号产生到被进程接收这一段过程。
信号集:
信号屏蔽字mask:设置哪一个信号被屏蔽,bit位与kill -l查询出的相对应。
bit1=1代表SIGHUP被屏蔽
未决信号集:当相应信号被屏蔽时,信号产生后,对应bit位被置1。bit位与kill -l查询出的相对应。
当mask bit1=1时,SIGHUP信号产生后,未决信号集的bit1=1
当信号被取走后,bit位被自动清零
信号相关函数
1、发送信号
//像指定进程发送信号
int kill(pid_t pid, int sig);
//给自己发送信号
int raise(int sig);
返回值:成功返回0,失败返回EOF
pid:进程号
pid值 | 含义 |
> 0 | 发送给指定的进程 |
= 0 | 发送给该进程所在的进程组中的进程 |
= -1 | 取绝对值,发送给指定进程所在的进程组中的所有进程 例如:pid = -2,那么发送给pid=2的进程所在的进程组中的进程 |
< -1 | 发送给所有进程 |
sig:信号类型,由kill -l 查出,比如要发送SIGINT,该值就为2
2、定时器
注意:一个进程中只能设定一个定时器,定时到达时产生SIGALRM信号
注意:有时sleep函数内部实现也会用到定时器,因此使用定时器时需要把sleep注释掉
2.1 一次性定时器
//一次性定时器
unsigned int alarm(unsigned int seconds);
返回值:成功返回上个定时器的剩余时间,失败返回EOF
seconds:定时时间,单位s
2.2 循环发送定时器
//循环发送定时器
useconds_t ualarm(useconds_t usecs, useconds_t interval);
usecs:第一次产生的时间,单位us
interval:之后定时结束的时间间隔,单位us
2.3 更通用的循环发送定时器
//更通用的循环发送定时器
int setitimer(int which, const struct itimerval *restrict value,struct itimerval *restrict ovalue);
which:选项,写入ITIMER_REAL,代表真正逝去的时间
value:新的超时时间
ovalue:旧的超时时间,写入NULL即可
struct itimerva结构体成员:
struct itimerval {struct timeval it_value; /* 闹钟触发时间 */struct timeval it_interval; /* 闹钟触发周期 */
};struct timeval {time_t tv_sec; /* 秒数 */suseconds_t tv_usec; /* 微秒数 */
};
3、捕捉信号自定义函数
3.1 signal(不建议使用)
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
返回值:成功返回原信号处理函数,失败返回SIG_ERR
signum:改变哪一个信号的行为
handler:信号行为改变成什么,SIG_DFL代表缺省,SIG_IGN代表忽略
示例代码如下:
具体代码实现如下:
#include <stdio.h>
#include <signal.h>
#include <errno.h>typedef void (*sighandler_t)(int);
void SIG_Handler(int);
int main(){if(signal(SIGINT,SIG_Handler) == SIG_ERR){perror("signal");}while(1);return 0;
}
void SIG_Handler(int sig){printf("this is SIG_Handler,SIG num = %d\n",sig);if(signal(SIGINT,SIG_DFL) == SIG_ERR){perror("signal");}
}
代码执行结果如下:
3.2 sigaction(建议使用)
int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
返回值:成功返回0,失败返回-1
signum:改变哪一个信号的行为
act:信号行为改变成什么
oldact:原信号处理函数,不关心可以写NULL
struct sigaction结构体成员:
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_handler:信号处理函数指针,这与signal的handler用法完全一致。
sa_mask:屏蔽位,使用 sigemptyset(&act.sa_mask) 去清空它即可。
sa_flags:设置回调函数选择哪一个函数指针
sa_flags值 | 含义 |
SA_SIGINFO | 信号回调函数使用sa_sigaction |
0 | 信号回调函数使用sa_handler |
示例1:实现signal相同的功能
#include <stdio.h>
#include <signal.h>
#include <errno.h>void SIG_Handler(int);
int main(){struct sigaction act;//设置信号处理函数act.sa_handler = SIG_Handler;//设置信号回调函数sigemptyset(&act.sa_mask); //清空屏蔽位act.sa_flags = 0; //0代表使用sa_handler作为信号回调函数//改变信号处理函数if(sigaction(SIGINT,&act,NULL) != 0){perror("sigaction");}while(1);return 0;
}
void SIG_Handler(int sig){struct sigaction act;act.sa_handler = SIG_DFL;//设为默认形式sigemptyset(&act.sa_mask); act.sa_flags = 0; printf("this is SIG_Handler,SIG num = %d\n",sig);if(sigaction(SIGINT,&act,NULL) != 0){perror("sigaction");}
}
示例2:实现定时器
实现定时器,首先设置好定时器发出的SIGALRM信号该如何处理,之后设置好定时器该如何定时即可。
具体代码实现如下:
#include <stdio.h>
#include <signal.h>
#include <errno.h>
#include <sys/time.h>void SIG_Handler(int);
int main(){struct sigaction act;struct itimerval value;//设置信号处理函数act.sa_handler = SIG_Handler;//设置信号回调函数sigemptyset(&act.sa_mask); //清空屏蔽位act.sa_flags = 0; //0代表使用sa_handler作为信号回调函数if(sigaction(SIGALRM,&act,NULL) != 0){perror("sigaction");}//设置定时器value.it_value.tv_sec = 5;//第一次定时5svalue.it_value.tv_usec = 0;value.it_interval.tv_sec = 1;//之后定时1svalue.it_interval.tv_usec = 0;printf("now time start\n");setitimer(ITIMER_REAL,&value,NULL);//启动定时器while(1);return 0;
}
void SIG_Handler(int sig){printf("time over,sig = %d\n",sig);
}
代码运行结果如下:
示例3:通过SIGCHLD信号回收子进程
回收子进程最终都要调用wait函数,但wait会进行阻塞直到子进程退出,通过捕捉SIGCHLD信号,可以使得父进程不进入阻塞,当子进程终止时中断父进程,从而实现不阻塞回收子进程。
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>void SIG_Handler(int);
int main(){pid_t pid;struct sigaction act;if((pid = fork())<0){perror("fork");}else if(pid == 0){while(1){printf("child is running\n");sleep(1);}}else{//设置信号处理函数act.sa_handler = SIG_Handler;//设置信号回调函数sigemptyset(&act.sa_mask); //清空屏蔽位act.sa_flags = 0; //0代表使用sa_handler作为信号回调函数if(sigaction(SIGCHLD,&act,NULL) != 0){perror("sigaction");}while(1){printf("father is running\n");sleep(1);}}return 0;
}
void SIG_Handler(int sig){int wstatus;printf("get SIG_Handler\n");waitpid(-1,&wstatus,WNOHANG);//以非阻塞方式,等待子进程结束if(WIFEXITED(wstatus)){ //判断子进程是否正常退出printf("子进程的返回值为%d\n",WEXITSTATUS(wstatus));}else{printf("子进程是否被信号结束%d\n",WIFSIGNALED(wstatus));printf("结束子进程的信号类型%d\n",WTERMSIG(wstatus));}
}
信号集相关函数
1、设置信号集
//自定义信号集
sigset_t set;
//清空信号集,即:全部写0
int sigemptyset(sigset_t *set);
//填充信号集,即:全部写1
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);
set:信号集
signum:信号类型,如:SIGINT
2、屏蔽信号
int sigprocmask(int how,const sigset_t *restrict set,sigset_t *restrict oset);
返回值:成功返回0,失败返回-1
how:信号设置后,做什么事情
注意:不能阻塞SIGKILL和SIGSTOP
指令 | 含义 |
SIG_BLOCK | 将set中的信号添加到信号屏蔽字中,即:信号阻塞 添加:最终结果 = set | oldset |
SIG_UNBLOCK | 解除信号阻塞,如果之前有信号产生,也会被检测到 |
SIG_SETMASK | 将信号屏蔽字设置为set 设置:最终结果 = set,与oldset无关 |
set:新的信号集
oset:旧的信号集,不关系可以写NULL
使用示例:
具体代码实现如下:
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#include <errno.h>void SIG_Handler(int);
int main(){struct sigaction act;sigset_t set;//设置信号处理函数act.sa_handler = SIG_Handler;//设置信号回调函数sigemptyset(&act.sa_mask); //清空屏蔽位act.sa_flags = 0; //0代表使用sa_handler作为信号回调函数if(sigaction(SIGINT,&act,NULL) != 0){perror("sigaction");}//控制信号集sigemptyset(&set); //清空信号集sigaddset(&set,SIGINT); //添加想要阻塞的信号sigprocmask(SIG_BLOCK,&set,NULL); //设置信号阻塞sleep(5);sigprocmask(SIG_UNBLOCK,&set,NULL);//设置信号不阻塞,即:响应信号while(1);return 0;
}
void SIG_Handler(int sig){printf(" get sig\n");
}
代码运行结果如下:
信号驱动任务
信号驱动任务的方式:
- 捕捉信号,改变信号处理函数(类似中断)
- 使用pause实现(不是中断,信号只是作为开始信号)
- 使用sigsuspend改善pause的实现代码
1、捕捉信号实现
捕捉信号实现就是"信号相关函数"-"3、捕捉信号自定义函数"中的示例代码使用方法。
2、 pause实现
//进程一直阻塞,直到被信号中断
int pause(void);
返回值:-1
pause的行为:
- 若信号默认行为是终止,则进程终止,pause没有机会返回
- 若信号默认行为是忽略,则进程继续阻塞,pause不返回
- 若信号处理动作为捕捉,则调用完信号处理函数后,pause返回-1
- 若信号被屏蔽,则进程继续阻塞,pause不返回(这其实就是没收到信号)
示例代码:
具体代码实现如下:
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#include <errno.h>void SIG_Handler(int);
void* Task(void* arg);
int main(){struct sigaction act;sigset_t set;int ret;//设置信号处理函数act.sa_handler = SIG_Handler;//设置信号回调函数sigemptyset(&act.sa_mask); //清空屏蔽位act.sa_flags = 0; //0代表使用sa_handler作为信号回调函数if(sigaction(SIGINT,&act,NULL) != 0){perror("sigaction");}//控制信号集sigemptyset(&set); //清空信号集sigaddset(&set,SIGINT); //添加想要阻塞的信号//信号驱动任务while(1){printf("now is waiting sig\n");ret = pause(); //等待信号到达sigprocmask(SIG_BLOCK,&set,NULL); //设置信号阻塞Task((void*)ret); //执行相应任务sigprocmask(SIG_UNBLOCK,&set,NULL);//设置信号不阻塞,即:响应信号}return 0;
}
void SIG_Handler(int sig){printf(" get sig\n");
}
void* Task(void* arg){printf("Now is Task Fun,arg = %d\n",(int)arg);sleep(5);
}
代码执行结果如下:
该情况可由"3、sigsuspend改善pause代码"实现
3、sigsuspend改善pause代码
int sigsuspend(const sigset_t *sigmask);
sigmask:想要屏蔽的信号,设置该值使用"信号集相关函数"-"1、设置信号集"中的函数。
该值被sigemptyset设置后代表全部信号都不屏蔽
示例代码:
具体代码实现如下:
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#include <errno.h>void SIG_Handler(int);
void* Task(void* arg);
int main(){struct sigaction act;sigset_t set,unblockSet;int ret;//设置信号处理函数act.sa_handler = SIG_Handler;//设置信号回调函数sigemptyset(&act.sa_mask); //清空屏蔽位act.sa_flags = 0; //0代表使用sa_handler作为信号回调函数if(sigaction(SIGINT,&act,NULL) != 0){perror("sigaction");}//控制信号集sigemptyset(&set); //清空信号集sigaddset(&set,SIGINT); //添加想要阻塞的信号//信号驱动任务printf("now is waiting sig\n");ret = pause(); //在while外使用pause等待第一次的信号sigemptyset(&unblockSet); //清空信号集,该信号集用于解除信号阻塞while(1){sigprocmask(SIG_BLOCK,&set,NULL); //设置信号阻塞Task((void*)ret);sigsuspend(&unblockSet); //解除信号阻塞并且可以让pause接收到阻塞的信号//该函数相当于sigprocmask和pause}return 0;
}
void SIG_Handler(int sig){printf(" get sig\n");
}
void* Task(void* arg){printf("Now is Task Fun,arg = %d\n",(int)arg);sleep(5);
}
代码执行结果如下: