信号
在Linux系统当中有62个信号,可以通过 kill -l 这个命令查看如下图所示
可以看到图中的信号分为两个风格1-31是有独自的名字,而34-64名字都是相同的。1-31是非实时信号,34-64是实时信号。关于信号在man手册中的第七本signal有详细的说明。
操作函数
signal:忽略信号,捕捉信号,恢复信号默认缺省动作函数
所需头文件:
#include <signal.h>
函数原型:
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
参数说明:
signum:需要进行操作的信号
handler:如果是一个函数则捕捉到信号后执行
SIG_IGN:忽略这个信号
SIG_DFL:默认执行这个信号的缺省动作
返回值:
返回信号处理程序的先前值,SIG_ERR则代表出错并将错误原因写入errno。
sigprocmask:阻塞信号(将信号挂起恢复信号后执行动作)函数
所需头文件:
#include <signal.h>
函数原型:
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
参数说明:
how:
SIG_BLOCK 添加信号集合里面的阻塞信号
SIG_UNBLOCK 解开阻塞
SIG_SETMASK 覆盖原本的设置,把set信号集里面的信号设置到里面去
set:信号集合(需要设置的信号的集合)
oldset:保存旧的设置,不需要保存则填NULL
返回值:
成功返回0,失败返回-1并将错误原因写入errno中。
信号集的操作函数:
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);
判断信号是否在信号集中。
非实时信号(1-31)
非实时:能响应就响应,响应不了就丢掉。尽可能执行多个
非实时信号有以下特点:
信号有可能会丢失,非实时信号的登记方式是通过进程结构体中的一个标志位(每个信号对应着一个二进制数),通过这个标志位来记录信号的个数,当接收到一个信号,在响应的过程中这时这个信号的标志位由0变为1。进程还未结束响应时,再次发送同一个信号给进程时这个信号的标志位由0变为1,这时当函数还未结束时再去发送n个相同的信号都无法进行登记从而造成信号的丢失。
嵌套响应(同一个信号不能连续嵌套),一个进程在响应信号的过程中会被另外的信号打断(非相同信号),这时进程会响应后面这个信号,响应完后面这个信号后再回过头去响应上一个信号,就是所谓的嵌套响应。
特定的响应模式,每个非实时响应信号的默认缺省动作都不相同且响应的事件也不相同有的例如SIGINT和SIGQUIT,一个默认缺省动作是中断,一个默认缺省是退出。
对应的名字和系统事件,非实时信号与实时信号相比比较明显的一点就是名字。每个非实时信号的名字有对应的名字且名字对应着系统事件(所实现的功能)。
响应的顺序不确定,一般情况下是先来后到的顺序,被堵塞时优先级低于实时信号。
实时信号(34-64)
实时:规定的时间响应完,且必定会响应。响应不了则报错。先来先执行。
信号不会丢失,实时信号的登记方式是通过变量来登记的,同一个变量可以登记多次不存在丢失的可能。
嵌套响应,同一个信号可以连续响应,响应方式与非实时 信号相同。
响应的顺序一般是先来后到的,只有在信号被阻塞的时候才会有优先级且由大到小。
小练习
忽略ctrl+c对程序的影响,当程序接收到信号SIGQUIT信号的时候打印一句话。
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void signal_fun(int handler)/*SIGQUIT响应函数*/
{
printf("Effort is the most important!\n");
}
int main(void)
{
if(SIG_ERR == signal(SIGINT,SIG_IGN))/*忽略SIGINT信号*/
{
perror("signal err");
return -1;
}
if(SIG_ERR == signal(SIGQUIT,signal_fun))/*捕捉SIGQUIT信号*/
{
perror("signal err");
return -1;
}
while(1)
{
printf("\n");
sleep(1);
}
if(SIG_ERR == signal(SIGINT,SIG_DFL))/*恢复SIGINT信号*/
{
perror("signal err");
return -1;
}
return 0;
}
编写一段程序,创建两个子进程:
用signal让父进程捕捉SIGINT,
当捕获到SIGINT后,父进程用kill向两个子进程分别发信号,
发送SIGUSR1给子进程1, 发送SIGUSR2给子进程2
子进程捕捉到父进程发来的信号后,分别输入下列信息后终止
Child process 1 is killed by parent!
Child process 2 is killed by parent!
父进程等待两个子进程终止后,输出以下信息后终止
Parent process exit!
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
int retvel[2],status;
void signal_fun(int handler)/*SIGINT响应函数*/
{
kill(retvel[0],SIGUSR1);
kill(retvel[1],SIGUSR2);
}
void signal_fun1(int handler)
{
printf("Child process 1 is killed by parent!\n");
}
void signal_fun2(int handler)
{
printf("Child process 2 is killed by parent!\n");
}
int main(void)
{
signal(SIGINT,SIG_IGN);
/*注意:此处必须忽略SIGINT,让子进程继承忽略中断信号,否则当主进程捕捉中断信号时子进程将被杀死*/
retvel[0] = fork();/*创建第一个子进程*/
if(retvel[0] == 0)
{
if(SIG_ERR == signal(SIGUSR1,signal_fun1))/*捕捉SIGUSR1信号*/
{
perror("signal err");
return -1;
}
pause();/*暂停函数,遇到任意一个信号则继续下一步*/
exit(EXIT_SUCCESS);/*正常退出*/
}
retvel[1] = fork();/*创建第二个子进程*/
if(retvel[1] == 0)
{
if(SIG_ERR == signal(SIGUSR2,signal_fun2))/*捕捉SIGUSR2信号*/
{
perror("signal err");
return -1;
}
pause();/*暂停函数,遇到任意一个信号则继续下一步*/
exit(EXIT_SUCCESS);/*正常退出*/
}
if(SIG_ERR == signal(SIGINT,signal_fun))
/*注意:此处定义捕捉中断信号将不被子进程所继承,定义在程序开头将会被子进程继承将会造成别的结果*/
{
perror("signal err");
return -1;
}
for(int i=0;i<2;i++)
{
wait(&status);/*等待两个子进程结束*/
}
printf("Parent process exit!\n");
return 0;
}
编写一个程序,该程序规定SIGINT这个信号接收的时候,每隔一秒打印一句hello,
其他所有的信号都不能打断SIGINT的响应函数(不能嵌套响应)(signal跟sigprocmask两个函数做)
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void signal_fun(int handler)/*中断响应函数*/
{
int i=5;
sigset_t set_signal;/*创建信号集*/
sigfillset(&set_signal);/*将所有信号添加进信号集*/
sigdelset(&set_signal,SIGINT);/*将中断信号删除出信号集*/
sigprocmask(SIG_BLOCK,&set_signal,NULL);/*为信号集设置阻塞*/
while(i--)
{
printf("SIGINT_response\n");/*每触发一次打印5句*/
sleep(1);/*每隔一秒*/
}
sigprocmask(SIG_UNBLOCK,&set_signal,NULL);/*执行完后清除阻塞*/
}
int main(void)
{
if(SIG_ERR == signal(SIGINT,signal_fun))/*捕捉中断信号*/
{
perror("signal err");
return -1;
}
while(1)
{
printf("hello\n");/*每隔一秒打印一句*/
sleep(1);
}
return 0;
}
常用命令
ps -aux
查看进程状态
pstree
以树状的形式查看进程状态
kill -s 参数1 参数2
参数1:指定发送的信号
参数2:指定接收的进程号
kill 进程号
默认发送SIGQUIT信号退出程序
有两个特殊的信号无法被忽略、堵塞或捕捉
SIGKILL(杀死) SIGSTOP(暂停)
结尾
这里是一只正在努力的菜鸟,如果有哪里写的不对的地方,希望各位大佬多多指正,我会加以改进!