1.知识点
信号的收发
信号的几种响应方式:
第一种:默认响应方式
第二种:程序员指定响应方式
第三种:信号的忽略
第四种:信号的阻塞(屏蔽)
2.引入信号
生活中的例子:大脑接收外界的红绿灯信号,控制大脑做不同的事情
linux中的进程:也能接收外界发送的信号,控制进程做出不同的行为
linux系统提供的信号
kill -l 查看所有的信号(62个信号)
2) SIGINT 导致程序终止
3) SIGQUIT 终止
9) SIGKILL 终止
18) SIGCONT 继续
19) SIGSTOP 暂停
linux提供两个常用命令用来给进程发送信号
第一个: kill命令
kill -信号的名字 进程的ID
kill -信号的序号 进程的ID
比如: kill -STOP 进程ID
kill -19 进程ID
第二个: killall命令
killall -信号的名字 进程的名字
killall -信号的序号 进程的名字
比如: killall -STOP 进程名字
killall -19 进程名字
3.linux跟信号有关的接口函数
总结信号的响应动作
第一种:默认动作(linux系统已经规定好的动作)
绝大部分信号都会终止你的进程
第二种:程序员可以改变信号的响应动作
注意:SIGKILL和SIGSTOP信号不能改变默认动作
第三种:程序员可以使用signal函数去忽略信号
第四种:程序员可以阻塞信号
(1)发送信号
#include <signal.h>
int kill(pid_t pid, int sig);
返回值:成功 0 失败 -1
参数: pid --》进程的ID
sig --》你要发送的信号的序号/名字
(2)捕捉信号,并改变信号的响应动作(重点)
void (*signal(int sig, void (*func)(int)))(int);
简化写法:void (*signal(参数1,参数2))(int);
再简化: void (*函数名字())(int);
signal(你要捕捉的信号,捕捉到这个信号以后做什么事情);
返回值:也是一个函数指针,类型跟第二个参数一样
参数:sig --》你要捕捉的信号
void (*func)(int) --》函数指针,用来表示信号的响应动作
参数: int --》你捕捉到的信号的序号
用法一:signal可以改变信号的响应动作
用法二:忽略信号SIG_IGN(左耳进右耳出,信号发过来,当作不存在)
注意:SIGKILL和SIGSTOP信号不能忽略
用法三:按照信号的默认动作响应SIG_DFL
(3)阻塞当前进程,等待信号的到来
int pause(void);
(4)另外一组信号的发送和捕捉函数
两组常用的信号发送和捕捉函数
第一组:kill()和signal()
第二组:sigqueue()和sigaction()
发送信号(携带额外数据,买一送一):
int sigqueue(pid_t pid, int sig, const union sigval value);
参数:前面两个跟kill参数一样
value --》存放你要发送给进程的额外数据
union sigval {
int sival_int; //发送一个整数给进程
void *sival_ptr; //发送一个地址给进程
};
捕捉信号,改变信号的响应动作
int sigaction(int sig, const struct sigaction *act, struct sigaction *oact);
参数:sig --》你要捕捉的信号
act --》
struct sigaction
{
void(*sa_handler)(int) //函数指针,跟signal的功能一模一样
sigset_t sa_mask //???
int sa_flags //标志位,设置0表示选择sa_handler作为信号的响应函数
设置SA_SIGINFO表示选择sa_sigaction作为信号的响应函数 void(*sa_sigaction) (int,siginfo_t *,void *); //函数指针
}
void(*sa_sigaction) (int,siginfo_t *,void *)
参数: int --》信号的序号
siginfo_t --》结构体,存放收到的额外数据
{
si_int; //对应额外数据中的那个整数
si_ptr; //对应额外数据中的那个指针
}
oact --》用来备份之前的设置,一般设置为NULL
(5)阻塞信号(屏蔽信号)
概念:当信号发送给进程的时候,该进程把信号挂起(暂时把信号挡住,不去响应该信号)
sigset_t类型:称之为信号阻塞掩码集(多个信号的一个集合,你把要屏蔽的信号存放到这个集合中,然后设置阻塞即可)
int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
参数:how --》SIG_BLOCK //设置信号阻塞
SIG_UNBLOCK //解除信号的阻塞
set --》存放你想阻塞的所有信号
oset --》备份
如何把需要阻塞的信号添加到集合中去???
int sigemptyset(sigset_t *set); //清空集合set
int sigfillset(sigset_t *set); //把linux中所有信号一次性加入到集合set中
int sigaddset(sigset_t *set, int signum); //把signum这个信号添加到集合set中
int sigdelset(sigset_t *set, int signum); //把signum这个信号从集合set中删除
int sigismember(const sigset_t *set, int signum); //判断signum这个信号在不在集合set中
是成员 返回1
不在集合中 返回0
#include "myhead.h"
int main(int argc,char **argv)
{
//通过主函数传参把另外一个进程的ID号传递过来
//主函数传参传递过来的都是字符串,但是ID要求是整数
//int atoi(const char *nptr); 可以把字符串转换成整数
kill(atoi(argv[1]),9); //发送9号信号SIGKILL
}
#include "myhead.h"
int main()
{
printf("该进程的ID是:%hu\n",getpid());
while(1)
{
printf("外界可以控制这个进程!\n");
sleep(1);
}
}
signal忽略信号
#include "myhead.h"
int main()
{
printf("程序已经跑起来了!\n");
//signal函数忽略一些信号
signal(SIGQUIT,SIG_IGN);
signal(SIGINT,SIG_IGN);
signal(SIGKILL,SIG_IGN); //KILL信号不能忽略,写了不起作用
signal(SIGSTOP,SIG_IGN); //STOP信号不能忽略,写了不起作用
//while(1); //老土的做法,防止程序退出
//阻塞进程,等待信号的到来
pause();
}
signal捕捉信号并改变信号的相应动作
#include "myhead.h"
//自定义函数表示信号的响应动作
void fun(int sig)
{
printf("我捕捉到信号了!信号的序号:%d\n",sig);
}
void otherfun(int sig)
{
printf("我捕捉到信号了!信号的序号:%d\n",sig);
}
int main()
{
printf("程序已经跑起来了!\n");
//signal函数去捕捉SIGQUIT,改变SIGQUIT信号的响应动作
signal(SIGQUIT,fun);
//signal函数去捕捉SIGINT,改变SIGINT信号的响应动作
signal(SIGINT,otherfun);
signal(SIGKILL,fun); //KILL信号不能改变默认动作
signal(SIGSTOP,fun); //STOP信号不能改变默认动作
while(1); //防止程序退出
}
信号的屏蔽
#include "myhead.h"
int main()
{
//第一步:定义sigset_t变量,把你要屏蔽的信号加入到该集合中
sigset_t myset;
//清空集合
sigemptyset(&myset);
//添加你想要屏蔽的信号--》2 3 4三个信号屏蔽
sigaddset(&myset,SIGINT);
sigaddset(&myset,SIGQUIT);
sigaddset(&myset,4);
sigaddset(&myset,SIGKILL); //不能屏蔽
sigaddset(&myset,SIGSTOP); //不能屏蔽
//第二步:调用sigprocmask函数设置信号屏蔽
sigprocmask(SIG_BLOCK,&myset,NULL);
pause();
}
信号屏蔽只是暂时把信号挂起,等你解除屏蔽信号依然可以响应
#include "myhead.h"
int main()
{
int i;
//第一步:定义sigset_t变量,把你要屏蔽的信号加入到该集合中
sigset_t myset;
//清空集合
sigemptyset(&myset);
//添加你想要屏蔽的信号--》2
sigaddset(&myset,SIGINT);
//第二步:调用sigprocmask函数设置信号屏蔽
sigprocmask(SIG_BLOCK,&myset,NULL);
//从下面这一行代码开始,不受SIGINT的影响
for(i=0; i<10; i++)
{
printf("我有金钟罩,你发送INT信号弄不死我!\n");
sleep(1);
}
//解除信号的屏蔽
sigprocmask(SIG_UNBLOCK,&myset,NULL);
printf("我还能正常运行吗?????\n");
for(i=0; i<3; i++)
{
printf("还能响应INT信号吗\n");
sleep(1);
}
pause();
}
sigqueue发送信息携带额外数据
#include "myhead.h"
int main(int argc,char **argv)
{
union sigval myval;
myval.sival_int=666; //给进程发送信号的同时,顺便发送一个整数
sigqueue(atoi(argv[1])+65536,2,myval);
}
sigaction捕捉信号采用和signal一模一样的功能
#include "myhead.h"
//自定义信号的响应函数
void fun(int sig)
{
printf("我捕捉的信号序号是:%d\n",sig);
}
int main()
{
struct sigaction myact;
bzero(&myact,sizeof(myact));
//myact.sa_handler=fun; //改变信号的响应动作
//myact.sa_handler=SIG_IGN; //忽略信号
myact.sa_handler=SIG_DFL; //按照信号的默认动作
myact.sa_flags=0;
//捕捉INT信号
sigaction(SIGINT,&myact,NULL);
pause();
}
sigaction捕捉信号接收额外数据
#include "myhead.h"
//自定义信号的响应函数
void fun(int sig,siginfo_t *info,void *arg)
{
printf("我捕捉的信号序号是:%d\n",sig);
printf("我收到的额外数据是:%d\n",info->si_int);
}
int main()
{
printf("进程的ID是:%hu\n",getpid());
struct sigaction myact;
bzero(&myact,sizeof(myact));
myact.sa_sigaction=fun;
myact.sa_flags=SA_SIGINFO;
//捕捉INT信号
sigaction(SIGINT,&myact,NULL);
pause();
}