一、关于信号的函数接口
1、如何发送信号给另外一个进程呢?(kill)
命令
kill -信号值 进程的ID号
killall -信号值 进程的名字
函数
kill - send signal to a process
//发送一个信号给一个进程
SYNOPSIS
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
参数:
pid 你要给哪个进程发送信号,将这个进程的ID号传递进来
sig 你要发送哪个信号,将这个信号的名字 或者 信号值 传递过来
返回值:
成功 返回 0
失败 -1
2、捕捉信号 signal() ---->man 2 signal
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler); --man 2 signal
上面两行展开可写成: void (*signal(int sig, void (*func)(int)))(int); --man 3 signal
解析: 返回值类型 这个是一个函数 返回值类型
void (* signal(int sig, void (*func)(int)) )(int);
函数作用: 捕捉一个指定的信号,去执行 信号响应函数
参数:
signum 你要捕捉哪个信号,将这个信号值 或者 信号的名字传递过来
handler 捕捉到这个信号之后,你要去做的事情(去执行这个函数,将这个函数的名字传递过来)
这个函数 的 类型 必须是 void func(int a)
返回值:
成功 返回 处理函数的地址(第二个参数)
失败 返回 SIG_ERR
注意:
1、所谓的捕捉信号 就是 获取当这个信号来之后,去执行 信号响应函数,原本的信号默认动作就不会执行了。
2、当调用signal 函数之后 ,程序不会阻塞,而是往下面代码执行,但是这个捕捉设置 是 全局有效
3、SIGKILL 、SIGSTOP 不能被捕捉,只能执行默认的动作
练习
进程1:发送信号
#include<stdio.h>
#include <sys/types.h>
#include <signal.h>
int main(int argc, char*argv[]) // ./a.out 3445
{
//获取你要发送的信号的进程的ID号
int id = atoi(argv[1]);
//发送信号
kill(id,SIGINT);
return 0;
}
进程二:
捕捉信号SIGUSR1 和 SIGUSR2,执行 对应的 信号响应 函数。
#include<stdio.h>
#include <sys/types.h>
#include <signal.h>
/*
练习1:
进程1: 发送信号
进程2: 捕捉信号SIGUSR1 和 SIGUSR2,执行 对应的 信号响应 函数
*/
//信号响应函数 ,也就是说signal捕捉到指定的信号之后,去执行这个函数
void signalHandle1(int arg) //arg 信号值
{
printf("arg:%d\n",arg);
printf("signalHandle1 听说你想杀死我\n");
}
void signalHandle2(int arg) //arg 信号值
{
printf("arg:%d\n",arg);
printf("signalHandle2 听说你想杀死我\n");
}
int main(int argc, char*argv[]) // ./a.out 3445
{
//捕捉信号SIGUSR1,去执行 信号响应函数
signal(SIGUSR1,signalHandle1);
//捕捉信号SIGUSR2,去执行 信号响应函数
signal(SIGUSR2,signalHandle2);
while(1)
{
}
return 0;
}
3、挂起进程,直到收到一个信号为止。 pause() --->man 2 pause
NAME
pause - wait for signal 将当前进程挂起,直到收到一个信号为止
SYNOPSIS
#include <unistd.h>
int pause(void);
#include<stdio.h>
#include <sys/types.h>
#include <signal.h>
int main(int argc, char*argv[]) // ./a.out 3445
{
printf("main start\n");
//将当前进程挂起,直到收到一个信号,程序才会往下面执行
pause();
printf("end\n");
return 0;
}
4、自己给自己发送信号。----》raise
#include <signal.h>
int raise(int sig);
#include<stdio.h>
#include <sys/types.h>
#include <signal.h>
//信号响应函数 ,也就是说signal捕捉到指定的信号之后,去执行这个函数
void signalHandle1(int arg) //arg 信号值
{
printf("arg:%d\n",arg);
printf("signalHandle1 听说你想杀死我\n");
}
int main(int argc, char*argv[]) // ./a.out 3445
{
printf("main start\n");
//捕捉信号SIGUSR1,去执行 信号响应函数
signal(SIGUSR1,signalHandle1);
while(1)
{
sleep(1);
//自己给自己发送信号SIGUSR1
raise(SIGUSR1);
}
return 0;
}
二、信号的处理(除了SIGKILL SIGSTOP)
1)忽略(将信号丢弃)
signal(signum,SIG_IGN)
2)缺省(执行默认的动作)
signal(signum,SIG_DFL)
3)捕捉(去执行 信号响应函数)
signal(signum,signalHandle)
4)阻塞(将信号挂起)
设置阻塞之后,来了阻塞的指定信号,并不是将信号丢弃,而且将信号挂起来,等到解除阻塞之后才去响应这个信号
注意: SIGKILL 和 SIGSTOP 这两个信号不能被忽略 、阻塞、捕捉 ,必须是执行默认动作
//忽略信号
#include<stdio.h>
#include <sys/types.h>
#include <signal.h>
int main(int argc, char*argv[]) // ./a.out 3445
{
printf("main start\n");
//捕捉信号SIGINT,然后 忽略掉这个信号
signal(SIGINT,SIG_IGN);
while(1)
{
}
return 0;
}
三、linux系统 信号集
1、什么是信号集
信号集是一个集合,这个集合中每个成员都是 信号,通过将信号加入到信号集中,再设置阻塞状态给信号集,那么整个信号集中里面所有的信号都会变成阻塞的状态。
2、信号阻塞 与 信号忽略 有什么区别?
信号响应: 收到信号之后,去执行 信号响应函数
信号忽略: 收到这个信号之后,直接丢弃这个信号。
信号阻塞: 进程在阻塞某一个信号的前提下,收到了这个信号之后,不会马上响应,而是挂起来,要等到解除阻塞之后,才会响应这个信号。
(放在一个挂起队列中)
四、信号集处理函数?
1、信号集变量 如何去定义 ?
类似定义一个数组 ,存储 里面要阻塞的多个信号
信号集 数据类型 sigset_t
比如 定义一个信号集变量 sigset_t set;
#include <signal.h>
int sigemptyset(sigset_t *set); //清空信号集
int sigfillset(sigset_t *set); //将Linux下的所有的信号都加入到信号集中set
int sigaddset(sigset_t *set, int signum); //在指定的信号集set中,添加一个指定的信号signum到集合中
int sigdelset(sigset_t *set, int signum);//在指定的信号集set中,从集合中删除一个指定的信号signum
int sigismember(const sigset_t *set, int signum);//判断一个指定的信号 signum 是否 在 集合 中
返回值 :
成功 返回 0
失败返回 -1
sigismember :如果有这个信号 返回 1
如果没有 返回 0
2、例子
例子:写一个程序,先清空信号集,再把SIGUSR1 SIGUSR2 信号添加到集合中,判断SIGUSR2信号是否在集合中
1)先定义一个信号集变量
2) 初始化(清空)
3)将信号 添加到集合中 SIGUSR1 SIGUSR2
4) 判断 SIGUSR2信号是否在集合中,如果在打印yes ,否则 打印 no
#include<stdio.h>
#include <sys/types.h>
#include <signal.h>
/*
例子:写一个程序,先清空信号集,再把SIGUSR1 SIGUSR2 信号添加到集合中,判断SIGUSR2信号是否在集合中
1)先定义一个信号集变量
2) 初始化(清空)
3)将信号 添加到集合中 SIGUSR1 SIGUSR2
4) 判断 SIGUSR2信号是否在集合中,如果在打印yes ,否则 打印 no
*/
int main(int argc, char*argv[]) // ./a.out 3445
{
//1)先定义一个信号集变量
sigset_t set;
//2) 初始化(清空)
sigemptyset(&set); //清空信号集
//3)将信号 添加到集合中 SIGUSR1 SIGUSR2
sigaddset(&set,SIGUSR1); //在指定的信号集set中,添加一个指定的信号signum到集合中
sigaddset(&set,SIGUSR2);
//4) 判断 SIGUSR2信号是否在集合中,如果在打印yes ,否则 打印 no
// sigismember 信号在集合中 返回1 否则 返回 0
if(sigismember(&set, SIGUSR2))
printf("yes\n");
else
printf("no\n");
return 0;
}
五、如何将信号集中的信号 全部设置为 阻塞状态?---》man 2 sigprocmask
#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
函数作用: 将信号集中的信号 设置 为 阻塞 状态 或者 解除 阻塞
参数:
how:
SIG_BLOCK-----》设置为阻塞的属性
SIG_UNBLOCK-----》解除阻塞
set: 你要设置 哪个信号集
oldset:原来的状态,如果不想获取原先设置的状态,一般我们设置为NULL
返回值:
成功 返回 0
失败 返回 -1
比如:将一个信号集设置为阻塞状态 ,注意对象是信号阻塞,而不是进程阻塞
sigset_t set;
sigprocmask(SIG_BLOCK,&set, NULL);
比如:解除阻塞
sigset_t set;
sigprocmask(SIG_UNBLOCK,&set, NULL);
1、练习1
创建一个子进程,父进程将SIGUSR1 加入到集合中,判断信号在不在集合中,再设置该信号为阻塞状态,
该状态持续10S(那么在10S内,对SIGUSR1都是阻塞的),10S后,解除阻塞,看看会不会响应信号?
子进程在10S内发送一个SIGUSR1给父进程。
主要看:信号发过去之后,如果是马上响应-------------信号响应
如果是10S后信号响应--------信号阻塞 ,对
如果是永远都不会响应 ------信号忽略
#include<stdio.h>
#include <sys/types.h>
#include <signal.h>
/*
例子:写一个程序,先清空信号集,再把SIGUSR1 SIGUSR2 信号添加到集合中,判断SIGUSR2信号是否在集合中
1)先定义一个信号集变量
2) 初始化(清空)
3)将信号 添加到集合中 SIGUSR1
4) 判断 SIGUSR2信号是否在集合中,如果在打印yes ,否则 打印 no
*/
void signalHandle(int arg)
{
printf("%d signalHandle\n",arg);
}
int main(int argc, char*argv[]) // ./a.out 3445
{
//将SIGUSR1信号 设置 一个信号响应函数
signal(SIGUSR1, signalHandle);
//1)先定义一个信号集变量
sigset_t set;
//2) 初始化(清空)
sigemptyset(&set); //清空信号集
//3)将信号 添加到集合中 SIGUSR1
sigaddset(&set,SIGUSR1); //在指定的信号集set中,添加一个指定的信号signum到集合中
//4) 判断 SIGUSR1信号是否在集合中,如果在打印yes ,否则 打印 no
// sigismember 信号在集合中 返回1 否则 返回 0
if(sigismember(&set, SIGUSR1))
printf("yes\n");
else
printf("no\n");
//将信号集中的所有的信号设置为 阻塞状态
//注意:设置的对象是信号,而不是进程,所以调用这个函数之后,程序还是会往下面走
sigprocmask(SIG_BLOCK,&set,NULL);
sleep(20);//延时20S
//再解除阻塞,也就是说,上面的20S之内,如果收到了信号SIGUSR1,那么进程会将这个信号挂起来,等到解除阻塞之后再执行信号响应函数
sigprocmask(SIG_UNBLOCK,&set,NULL);
return 0;
}
2、练习
#include<stdio.h>
#include <sys/types.h>
#include <signal.h>
#include <stdlib.h>
/*
练习4:验证阻塞属性会被子进程继承。
(假设父进程对某一个信号 SIGUSR1是阻塞的,那么带着这个状态产生的小孩,这个小孩是不是也对这个信号是阻塞的。)
*/
void signalHandle(int arg)
{
printf("%d signalHandle\n",arg);
}
int main(int argc, char*argv[]) // ./a.out 3445
{
//将SIGUSR1信号 设置 一个信号响应函数
signal(SIGUSR1, signalHandle);
//1)先定义一个信号集变量
sigset_t set;
//2) 初始化(清空)
sigemptyset(&set); //清空信号集
//3)将信号 添加到集合中 SIGUSR1
sigaddset(&set,SIGUSR1); //在指定的信号集set中,添加一个指定的信号signum到集合中
//4) 判断 SIGUSR1信号是否在集合中,如果在打印yes ,否则 打印 no
// sigismember 信号在集合中 返回1 否则 返回 0
if(sigismember(&set, SIGUSR1))
printf("yes\n");
else
printf("no\n");
//将信号集中的所有的信号设置为 阻塞状态
//注意:设置的对象是信号,而不是进程,所以调用这个函数之后,程序还是会往下面走
sigprocmask(SIG_BLOCK,&set,NULL);
//上面把信号 设置 成 阻塞状态之后,再创建子进程
pid_t id = fork();
if(id >0)//父进程
{
}
else if(id == 0)//子进程
{
printf("child :%d\n",getpid());
sleep(20);//延时20S
//再解除阻塞,也就是说,上面的20S之内,如果收到了信号SIGUSR1,那么进程会将这个信号挂起来,等到解除阻塞之后再执行信号响应函数
sigprocmask(SIG_UNBLOCK,&set,NULL);
exit(0);
}
//等待子进程退出
wait(NULL);
return 0;
}