信号
信号是系统预先编译好的某些特定的事件,信号可以被产生,可以被接收,产生和接收的主体是进程。
信号的特点:1.简单 2.携带的信息量少 3.使用在某些特定的场景中
信号的状态
1.产生状态 键盘:ctrl + c 命令:kill 系统函数 :kill() 软条件:定时器
2.未决状态 等待被处理,没有处理之前就是未决状态
3.递达状态
信号递达状态的三种响应方式
忽略:SIG_ING
默认:SIG_DFL
fun:自己写的信号处理函数
信号的处理优先级:信号的优先级比较高,当进程收到一个信号时,会先中断现在正在处理的事情优先处理信号,信号处理完后在继续刚才中断的工作
查看信号的种类:kill -l 或者 man 7 signal
信号相关的函数
1.kill 给某个进程发送信号
例如:int kill(pid_t pid,int sig) pid 值给哪个进程发送信号,sig是发送的具体信号
pid > 0 指定将信号发送给那个进程
pid == 0 信号被发送到和当前进程在同一个进程组的进程
pid == -1 将信号发送给系统上有权限发送的所有的进程
7 int main()
8 {
9 pid_t pid = fork();
10
11 if(pid > 0)
12 {
13 while(1)
14 {
15 printf("parent process = %d\n",getpid());
16 sleep(1);
17 }
18
19 }
20 else if(pid == 0)
21 {
22
23 sleep(2);
24 kill(getppid(),SIGKILL); //getppid() 获取父进程pid
25 }
26 return 0;
27 }
父进程每一秒打印一个parent process,子进程在两秒之后向父进程发送kill函数,终止父进程
2.raise 给自己发送信号
用kill封装一个raise函数:kill(getpid(),int sig);
函数原型:int raise(int sig)
8 int main()
9 {
10 pid_t pid = fork();
11 if(pid > 0)
12 {
13 printf("parent process pid = %d\n",getpid()); // 打印父进程自己的pid getpid()是用来获取当前进程的pid
14 int s;
15 pid_t wpid = wait(&s); //用来监测子进程被杀死的返回值
16
17 printf("child died pid = %d\n",wpid);
18 if(WIFSIGNALED(s)) //监测是不是被信号终止
19 {
20 printf("died by signal = %d\n", WTERMSIG(s)); //如果是被信号终止,则打印信号的值
21 }
22
23 }
24 else if(pid == 0)
25 {
26 raise(SIGQUIT); //自己给自己发送3号函数 ctrl + \ 来终止程序
27 }
28 return 0;
29 }
该父进程是被子进程的SIGQUIT(3号信号)杀死的,所以打印的是3
3.abort 异常终止信号
异常终止,打印出来的信号一定是6
8 int main()
9 {
10 pid_t pid = fork();
11 if(pid > 0)
12 {
13 printf("parent process pid = %d\n",getpid()); //打印父进程pid
14 int s;
15 pid_t wpid = wait(&s);
16
17 printf("child died pid = %d\n",wpid);
18 if(WIFSIGNALED(s))
19 {
20 printf("died by signal = %d\n", WTERMSIG(s)); //被异常终止信号终止的,所以打印6
21 }
22
23 }
24 else if(pid == 0)
25 {
26 abort(); //异常终止信号(6号信号),终止子进程
27 }
28 return 0;
29 }
4.alarm 设定定时器(每个进程只有一个定时器)
使用的是自然定时法,不受进程状态影响
函数原型:unsigned int alarm(unsigned int seconds);
参数:秒
当事件到达一个信号之后,函数发出一个信号:SIGALRM
7 int main()
8 {
9 alarm(1); //定时器设定一秒
10 int i = 0;
11 while(1)
12 {
13 printf("%d\n",i++); //在这一秒内打印i的值,可以看出计算机一秒内打印的数值的多少
14 }
15 return 0;
16 }
在这里存在一个问题,就是如果讲打印的i的值重定向到文件中,则打印的值会比打印到终端的值多很多
realtime = 用户time+内核time+损耗time
损耗来自文件IO操作,IO操作会消耗大量的时间,所以数的少
5.概念:阻塞信号集,未决信号集
1.阻塞信号集:要屏蔽的信号
要阻塞某个信号,必须先设置阻塞信号集
2.未决信号集:没有被处理的信号的集合
信号产生,处于未决状态,进程收到信号之后,信号被放入未决信号集。 放入未决信号集中的信号等待处理,在处理之前需要做一件事情,判断阻塞信号集中的标志位是否是1,如果是1,不处理,如果是0,则处理
6.自定义信号集
int sigemptyset(sigset_t *set) 将set集合置空
int sigfillset(sigset_t *set) 将所有信号加入set集合
int sigaddset(sigset_t *set,intsigno) 将signo信号加入到set集合
int sigdelset(sigset_t *set,int signo) 从set集合中移除signo信号
int sigismember(const sigset_t *set,int signo) 判断信号是否存在
7.sigprocmask函数
屏蔽and接触信号屏蔽,将自定义信号集设置给阻塞信号集
函数原型:int sigprocmask(int how,const sigset_t *set,sigset_t *oldset)
8.sigpending 读取当前进程的未决信号集
函数原型:int sigpending(sigset_t *set)
参数:set 内核将未决信号集写入set
8 int main()
9 {
10 //创建自定义信号集合
11 sigset_t myset;
12 //将set集合致空
13 sigemptyset(&myset);
14 //将所有信号加入set集合
15 sigfillset(&myset);
16 //给自定义集合设置需要屏蔽的信号
17 sigaddset(&myset,SIGINT); //把2信号添加到自定义集合中
18 sigaddset(&myset,SIGQUIT); //把3信号添加到自定义集合中
19 sigaddset(&myset,SIGKILL); //把9信号添加到自定义集合中
20 //将自定义的信号集设置给内核的阻塞信号集
21 sigprocmask(SIG_BLOCK,&myset,NULL);
22
23 while(1)
24 {
25 //读取当前信号的未决信号集
26 sigset_t pendset;
27 sigpending(&pendset);
28
29 //循环监听1-32号信号,看是否存在,存在打印1,不存在打印0
30 int i = 1;
31 for(;i < 32;i++)
32 {
33 if(sigismember(&pendset,i)) //如果有该信号,就打印1,否则打印0
34 {
35 printf("1");
36 }
37 else
38 {
39 printf("0");
40 }
41
42 }
43
44 printf("\n");
45 sleep(1);
46 }
47 return 0;
48 }
这里存在一个问题,就是打印的时候9好信号一直不会被监测到,这就牵扯到了SIGKILL的特性,不能被忽略,不能被阻塞,也不能被捕捉
the signals SIGKILL and SIGSTOP cannot be caught,blocked,ignored
9.信号的捕捉
1.signal函数
函数原型:typedef void(*sighandler_t)(int) 函数指针;回调函数
sighandler_t signal(int signum,sighandler_t handler)
7 //设置回调函数
8 void function(int num)
9 {
10 printf("catch signal = %d\n",num); //捕捉到哪个信号就会打印哪个信号的值,被捕捉到之后也就不能发生作用了,所以在这里按ctrl+c不会终止进程,除非按ctrl+\或者kill
11 }
12
13 int main()
14 {
15 // 捕捉信号
16 //注册捕捉函数
17 signal(SIGINT,function); //监测到SIGINT该信号就会调用回调函数function
18
19 while(1)
20 {
21 printf("hello, I'm here!\n");
22 sleep(2);
23 }
24 return 0;
25 }
2.sigaction 临时处理信号
函数原型:int sigaction(int signum,const struct sigaction *act,struct sigaction * oldact)
第二个参数
struct sigaction{
void (*sa_handler)(int);
void (*sa_sigaction)(int,siginfo_t*,void*);
sigset_t sa_mask; //在信号处理函数执行过程中,临时处理指定的信号
int sa_flags; //0 -> 调用sa_handler sa_flagde值赋值0
};
7 void myfun(int num)
8 {
9 printf("catch signal = %d\n",num);
10 sleep(4); //在函数睡眠的时候,会忽略你键入的信号,等到睡醒之后才会执行你键入的操作
11 printf("game over!\n");
12 }
13
14 int main()
15 {
16 //设置struct sigaction *act结构体
17 struct sigaction act;
18 act.sa_flags = 0;
19
20 //先清空信号的集合act.sa_mask,然后添加需要捕捉的信号
21 sigemptyset(&act.sa_mask);
22 sigaddset(&act.sa_mask,SIGINT);
23 act.sa_handler = myfun;
24 sigaction(SIGQUIT,&act,NULL);
25
26 while(1);
27 return 0;
28
29 }