linux笔记之初次接触信号

来源:互联网 发布:mac优酷缓存视频路径 编辑:程序博客网 时间:2024/06/03 12:19

一.关于信号概念


1.信号是Linux所使用的进程间通信的最古老的方式。它是在软件层次上对中断机制的一种模拟,是一种异步通信的方式 。一个完整的信号周期包括三个部分,信号的产生,信号在进程中的注册,信号在进程中的注销,执行信号处理函数。如下图所示:这里写图片描述

这里的对信号产生注册和注销都是信号的内部机制,而不是信号函数完成的

2.对信号的处理动作有三种:

a. 忽略此信号

b.执行该信号的默认处理动作

c.捕捉信号(自由态)

3.查看系统定义中的信号列表: kill -l 命令

这里写图片描述
每个信号都有一个编号和一个宏定义名称,这些宏定义名称可以在signal.h中找到,例如:其中有定义#define SIGINT 2

二.产生信号

1. 通过终端按键产生信号

如下代码:

#include<stdio.h>#include<signal.h>void hander(int sig){    printf("%d\n",sig);}int main(){    signal(2,hander);    while(1)    {        printf("i am a proc\n");        sleep(1);    }}

这里写图片描述
以上代码使用了signal函数用来捕捉信号,则可以说明的是:ctrl+c实现的是信号的2号信号SIGINT。

2.调用系统函数向进程发信号

介绍几个函数

  • kill命令是调用kill函数实现的。kill函数可以给一个指定的进程发送指定的信号。
#include<signal.h>int kill(pid_t pid,int signo);
  • raise函数可以给当前进程发送指定的信号(自己给自己发信号)
#include<signal.h>int raise(int signo);

如下两个代码实现raise函数的功能:

#include<stdio.h>#include<signal.h>#include<sys/types.h>#include<stdlib.h>void hander(int sig){    printf("%d\n",sig);}int main(){     signal(2,hander);     sleep(3);     while(1)     {       raise(2);        printf("i am a proc\n");        sleep(1);    }}

结果如下图:

这里写图片描述

#include<stdio.h>#include<signal.h>#include<sys/types.h>#include<stdlib.h>void hander(int sig){    printf("%d\n",sig);}int main(){     signal(2,hander);     sleep(3);     raise(2);     while(1)     {        printf("i am a proc\n");        sleep(1);    }}

结果如下图:

这里写图片描述

  • abort函数可以使当前进程接收到信号而异常终止,像exit函数一样,abort函数总会成功,故无返回值
#include<stdlib.h>void abort(void);

3.由软件条件产生信号

例如:模拟闹钟

调用alarm函数可以设定一个闹钟,告诉内核在seconds秒之后给当前进程发SIGALRM信号,该信号的默认动作是终止当前程序

该函数的返回值是0或者是以前设定的闹钟时间还余下的秒数

用以下代码实现alarm的功能:

代码1:

#include<stdio.h>#include<unistd.h>int main(){    alarm(1);    int count=0;    for(;1;count++)    {    printf("count= %d\n",count);    }    return 0;}

结果示意图:

这里写图片描述

###代码2:

#include<stdio.h>#include<signal.h>#include<sys/types.h>#include<stdlib.h>#include<unistd.h>int count=0;void hander(int sig){    printf("count = %d\n",count);}int main(){    signal(14 ,hander);    alarm(1);    while(1)    {        count++;    }    return 0;

结果示意图:

这里写图片描述

以上两个代码均是表示count 在1 秒累加的次数,从结果看出第二个数比第一个数大很多,所以说明输入输出流对计算的速度影响非常大

三.阻塞信号


1. 信号在内核中的表示

a.实际执行信号的处理动作叫信号递达(Delivery),信号从产生到递达的状态,称为信号未决(Pending).进程可以选择阻塞某个信号。

b.信号阻塞就不会被递达,除非信号先被未决,然后解除阻塞。

c.信号在那内核中的表示示意图:

这里写图片描述

2.信号集操作函数

#include<signal.h>int sigemptyset(sigset_t *set);//初始化set所指向的信号集,使其所有的bit清零,表示该信号集不包含任何有效信号。int sigfillset(sigset_t *set);//初始化set所指向的信号集int sigaddset(sigset_t *set,int signo);//在信号集中添加某种信号int sigdelset(sigset_t *set,int signo);//在信号集中删除某种信号int sigismemset(const sigset_t *set,int signo);//是一个布尔函数,用于判断一个信号集的有效信号中是否包含某种信号,包含返回1,反之返回0;

3.sigprocmask

#include<signal.h>int sigprocmask(int how,const sigset *set,sigset_t *oset);//可以读取和改进进程的信号屏蔽字(阻塞信号集)

4.sigpending

#include<signal.h>int sigpending(sigset_t *set);//读取当前进程的未决信号集,通过set参数传出

使用以上函数模拟一个信号在内核中的表示

#include<stdio.h>#include<signal.h>#include<unistd.h>void show(sigset_t* pending){    int i=1;    for(; i<32; i++)    {        if(sigismember(pending,i))        {            printf("1 ");        }        else        {            printf("0 ");        }    }    printf("\n");}int main(){    sigset_t block;    sigset_t oblock;    sigemptyset(&block);    sigemptyset(&oblock);    sigfillset(&block);    sigfillset(&oblock);    sigaddset(&block,2);    sigprocmask(SIG_BLOCK,&block,&oblock);    sigprocmask(SIG_BLOCK,&oblock,NULL);    while(1)    {        sigpending(&block);        show(&block);        sleep(1);    }    return 0;}

结果示意图:

这里写图片描述

程序运行时候,每秒钟都把各个信号的未决状态打印一遍,由于我们阻塞了SIGINT信号,故按CTRL+C将会使SIGINT信号处于未决状态,按CTRL+\则可以终止程序,因为SIGQUIT信号没有被阻塞。

1 0