概念:信号不是信号量,信号量是进程间的一种通信方式,信号是系统中的软件中断,指一种事件通知机制,通知进程发生了某个事件,打断当前的操作,去处理这个事件。
种类:一共有62种信号,可以用kill -l 选项进行查看
1-31是非可靠非实时信号。
34-64是可靠实时信号。
产生:
硬件产生:ctrl + c ctrl + z ctrl + \
ctrl + c 其实发送的就是2号信号 SIGINT 2号中断信号
ctrl + z 其实发送的是19号信号SIGSTOP19号停止信号
ctrl + \ 其实发送的是3号信号SIGQUIT3号退出信号
举个例子:
#include<stdio.h>
#include<string.h>
int main()
{
char * ptr = NULL;
strcpy(ptr,"hello");
return 0;
}
NULL是一个不可读不可写的地址我们向其中写入数据肯定是会发生错误的,程序也会奔溃的那咋样操作系统通知和处理这个事件呢?就是信号!
这里就是告诉你内存访问错误,发生了段错误。
软件产生:
kill 杀死进程的原理就是给进程发送了一个15号信号SIGTERM终止信号
kill
函数原型 int kill(pid_t pid, int sig);
pid_t pid 要发送信号的进程,int sig 要发送几号信号
#include<stdio.h>
#include<unistd.h>
#include<signal.h>
int main()
{
kill(getpid(),2);
while(1)
{
printf("_________________\n");
sleep(2);
}
return 0;
}
程序一运行就直接退出了,因为刚刚跑起来就调用kill这个函数了,我们可以借用gdb看看最后的函数调用栈来看看。
栈顶退出函数就是造成程序异常的函数,我main函数要等到return 了之后才算函数调用结束还没等到你return kill已经发送了一个二号信号来将你终止你此时只能就退出了。
raise
函数原型:int raise(int sig)
给进程自身发送一个信号。
#include<stdio.h>
#include<unistd.h>
#include<signal.h>
int main()
{
//kill(getpid(),2);
raise(SIGINT);
while(1)
{
printf("_________________\n");
sleep(2);
}
return 0;
}
这个SIGINT其实也是一个宏
也中断了。gdb
abort
函数原型:void abort(void);
给进程发送一个SIGABRT信号,异常信号。
#include<stdio.h>
#include<unistd.h>
#include<signal.h>
#include<stdlib.h>
int main()
{
//kill(getpid(),2);
//raise(SIGINT);
abort();
while(1)
{
printf("_________________\n");
sleep(2);
}
return 0;
}
alarm
函数原型: unsigned int alarm(unsigned int seconds);
定义一个计时器,在seconds秒之后给进程发送一个SIGALRM信号
#include<stdio.h>
#include<unistd.h>
#include<signal.h>
#include<stdlib.h>
int main()
{
//kill(getpid(),2);
//raise(SIGINT);
//abort();
alarm(3);
while(1)
{
printf("_________________\n");
sleep(1);
}
return 0;
}
sigqueue
函数原型:int sigqueue(pid_t pid, int sig, const union sigval value);
通知pid进程发生了啥事情并且传递一个数据
#include<stdio.h>
#include<unistd.h>
#include<signal.h>
#include<stdlib.h>
int main()
{
//kill(getpid(),2);
//raise(SIGINT);
//abort();
//alarm(3);
union sigval val;
val.sival_int = 10;
sigqueue(getpid(),SIGINT,val);
while(1)
{
printf("_________________\n");
sleep(1);
}
return 0;
}
注册:
让进程知道自己收到了信号
在进程pcb中有一个pending位图,未决信号集合,也就是当前收到的但没有处理的信号集合。信号的注册也就是在这个位图中标出对应位置为1,并且在pcb中有一个sigqueue链表,在这个链表中添加对应的信号节点。
可靠信号的注册:不管当前是否有相同信号已经注册,位图置1,添加一个节点。
非可靠信号的注册:如果相同信号没有杯注册那就注册一下,否则直接返回。
非可靠这种方式就有可能存在事件丢失这种情况。
注销:在处理之前将信号的存在痕迹抹除。
将sigqueue链表中的对应信号节点删掉,修改位图
可靠信号:删除一个节点当没有相同信号节点时再去修改位图
非可靠信号:删除信号的节点,直接位图置0
处理:处理一个信号就是调用信号的处理函数。
处理方式:
默认处理方式:系统中定义好的处理方式。
忽略处理方式:信号的处理方式就是不作为。
自定义处理方式:用户自定义一个处理函数,然后替换掉内核中默认的处理方式。
signal
函数原型: typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
signum:指定的信号值;
handler:新的处理方式 SIG_DFL 默认处理方式 SIG_IGN忽略处理方式
返回值是原先函数的处理方式。
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include<signal.h>
int main()
{
signal(2,SIG_IGN);
while(1)
{
printf("哎哟,你干嘛~~~~~\n");
sleep(2);
}
return 0;
}
这下我ctrl + c就停止不了程序了
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include<signal.h>
void sigcb(int signo)
{
printf("收到%d号信号\n",signo);
alarm(3);
}
int main()
{
//signal(2,SIG_IGN);
signal(SIGALRM,sigcb);
alarm(3);
while(1)
{
printf("哎哟,你干嘛~~~~~\n");
sleep(2);
}
return 0;
}
就给了一个自定义的处理方式。
测试一下返回值,我们知道signal处理完事之后返回的值是之前的处理方式。
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include<signal.h>
__sighandler_t func;
void sigcb(int signo)
{
printf("收到%d号信号\n",signo);
//alarm(3);
signal(signo,func);
}
int main()
{
func = signal(2,sigcb);
//signal(SIGALRM,sigcb);
//alarm(3);
while(1)
{
printf("哎哟,你干嘛~~~~~\n");
sleep(1);
}
return 0;
}
第二次ctrl + c已经退出来了说明函数返回值的作用重新返回2号信号的处理方式!
自定义处理方式的信号捕捉流程:
1.信号的处理是程序从内核态切换到用户态之前就已经处理完成的,
2.信号的处理是一次性把所有信号处理完毕后才返回主控流程的。
阻塞:阻塞一个信号就是暂时先不处理这个信号。
在pcb中有一个信号集合block信号阻塞集合,在集合中标记那个信号就表示要阻塞哪个信号,意味着收到这个信号暂时先不处理。
sigpromask
函数原型:int sigpromask(int how, sigset_t *set , sigset_t *old);
how:要对阻塞集合进行的操作
SIG_BLOCK:将set集合中的信号添加到block信号中去
SIG_UNBLICK:将set集合中的信号从block中移除
SIG_SETMASK:将set集合中的信号设置为block集合中的信号
set:要操作的信号集合
old:用于保存修改前block信号集合中的数据
int sigemptyset(sigset_t *set);清空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);判断指定信号是否在集合中
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include<signal.h>
void sigcb(int no)
{
printf("收到了%d号信号\n",no);
}
int main()
{
signal(2,sigcb);
signal(40,sigcb);
sigset_t set;
sigset_t old;
__sigemptyset(&set);
sigaddset(&set,2);
sigaddset(&set,40);
sigprocmask(SIG_BLOCK,&set,&old);
printf("enter continue!!\n");
getchar();
printf("continue!!\n");
sigprocmask(SIG_SETMASK,&old,NULL);
while(1)
{
sleep(1);
}
return 0;
}
在信号中有两个信号比较特殊:SIGKILL -9 和 SIGSTOP-19,这两个信号不会被阻塞不会被忽略不会被自定义,也就是这两个信号的处理方式是无法被修改的