【Linux】linux进程--进程信号

目录

1.信号的概念

2.信号的产生

3.信号的种类

4.信号的处理方式

5.信号的注册

6.信号的注销

7.信号的自定义处理方式

7.1.概念

7.2.函数

7.2.1.sighandler_t  signal(int  signum , sighandler_t  handler);      //自定义信号的调用函数

7.2.2.typedef  void  (*signhandler_t)(int);       //自定义信号

7.2.3.int  sigaction(int  signum , const  struck  sigaction  *act , struct  sigaction  *oldact);

8.信号的捕捉

8.1.捕捉的过程

8.2.信号的处理时机

8.3.处理信号的时候,不同的处理方式

9.信号的阻塞

9.1.要理解的点

9.2.再次理解信号的处理时机

9.3.接口


1.信号的概念

信号是一个软件中断:只是告诉你有这样一个信号,但具体信号如何传递信息,怎么处理什么时候处理是由进程决定的,所以是软中断。

2.信号的产生

硬件产生:

     ctrl+c:2号信号--SIGINT

     ctrl+z:20号信号--SIGTSTP

     ctrl+|:3号信号--SIGQUIT

软件产生:

     kill函数:

int  kill(pid_t  pid , int  sig);

给pid进程发送的sig信号,例如:kill(getpid(),2);

     raise函数:

int  raise(int sig);

谁调用给谁发送信号

kill命令:

     kill  -[信号值]  pid

3.信号的种类

非可靠信号(非实时信号):1 ~ 31

可靠信号(实时信号):34 ~ 64

总共有62个信号,两类信号的差别就是实时信号不会丢失信号,非实时信号可能会丢失信号。

4.信号的处理方式

1)操作系统对信号的处理方式(man 7 signal)

 信号可以分为五种动作term、core、ign、stop、cont

每一个硬件产生的信号都分在这五大类动作之下,有着各自的功能。

默认处理方式:SIG_DFL,操作系统当中已经定义好信号的处理方式了;

忽略处理方式:SIG_IGN,该信号为忽略被处理(例如僵尸进程的形成过程)

在这里结合进程信号的内容,更新一下僵尸进程形成原因的回答模板:

     子进程先于父进程退出,子进程在退出的时候,会告知父进程,父进程接收到消息之后是忽略处理的。会向父进程发送SIGCHLD信号,而SIGCHLD信号的处理方式为SIG_IGN忽略处理!导致了父进程没有回收子进程的退出状态信息,从而致使子进程变成了僵尸进程。

2)自定义处理方式:程序员可以更改信号的处理方式,定义一个函数,当进程收到该信号的时候,调用程序员自己写的函数。

5.信号的注册

基础概念:

一个进程接收到一个信号,这个过程称之为注册;信号的注册和注销并不是一个过程,是两个相互独立的过程。

每一个进程都拥有自己独立的注册位图和sigqueue队列

而信号的注册,就是将信号在位图中对应的比特位改成1,添加sigqueue节点到队列当中去

区分:

     非实时信号:

          第一次注册:修改sig位图(0->1),添加sigqueue节点

          第二次注册:同一个非实时信号,在前一个还没有被处理的情况下,再次注册,会修改sig                                   位图(1->1),但是不会再添加sigqueue节点

     实时信号:

          第一次注册:修改sig位图(0->1),添加sigqueue节点

          第二次注册:修改sig位图(1->1),并再次添加sigqueue节点

6.信号的注销

非可靠信号:

     1)将信号对应的sig位图当中的比特位置0;

     2)将对应的信号的sigqueue节点进行出队操作。

可靠信号

     1)将对应的信号的sigqueue节点进行出队操作;

     2)判断sigqueue队列当中还有没有相同的信号节点:如果有则比特位不置0;反之比特位置0。

7.信号的自定义处理方式

7.1.概念

自定义处理方式就是让程序员(使用者)自己定义某一个信号的处理方式。

7.2.函数

7.2.1.sighandler_t  signal(int  signum , sighandler_t  handler);      //自定义信号的调用函数

sighandler_t  handler:一个函数指针,接收一个函数,没有返回值,参数类型是int;将信号的处理方式替换成为函数指针所保存的函数地址,对应的函数。

参数:

     signum:信号值

     handler:更改为哪一个函数处理,接受一个函数地址

7.2.2.typedef  void  (*signhandler_t)(int);       //自定义信号

void sigcallback(int sig)    //定义信号
{
  ...
}
signal(2,sigcallback);       
//调用,当执行signal函数的时候,并不是调用sigcallback函数,而是将sigcallback函数的地址保存在函数指针当中

9号信号(强杀信号):是不能被自定义处理的

咱们写一串代码来更改二号终止信号ctrl+c试试看

代码如下:

  1 #include<stdio.h>                                                     
  2 #include<unistd.h>
  3 #include<signal.h>
  4 void sigcallback(int sig){
  5   printf("sigcallback func recv sig:%d\n",sig);
  6 }
  7 int main()
  8 {
  9   signal(SIGINT,sigcallback);   //SIGINT就是crtl+c给出的中断信号,直接改成咱们的自定义信号
 10   while(1)                      //while(1),sleep(1)进程
 11   {
 12     printf("i am main func,sleep(1)\n");
 13     sleep(1);
 14   }
 15   return 0;
 16 }

运行结果: 

果然无法终止while(1)进程,改写成了输出一段小废话了...

7.2.3.int  sigaction(int  signum , const  struck  sigaction  *act , struct  sigaction  *oldact);

参数:

     signum:信号值

     act:将信号的处理方式更改为act       //输入型参数

     cldact:原来信号的处理方式              //输出型参数

struct  sigaction{

     void  (*sa_handler)(int);     //保存信号处理方式(默认)的函数指针

     void  (*sa_sigaction)(int , siginfo_t  * , void  *);     //(也是保存信号的处理方式的函数指针),但是没有使用。当要使用的时候,配合sa_flags一起使用。当sa_flags的值为SA_SIGINFO的时候,信号按照sa_sigaction保存的函数地址进行处理。

     sigset_t  sa_mask;     //当进程在处理信号的时候,如果还在收到信号。则放倒该信号位图当中,后续放到进程的信号位图当中。

     int  sa_falgs;

     void  (*sa_restorer)(void);     //保留字段

};

这个函数也是更改自定义函数的一个方法,代码举例:

  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<signal.h>
  4 struct sigaction oldact;                                              
  5 void sigcallback(int sig){
  6   printf("sigcallback recv sig num:%d\n",sig);
  7   sigaction(2,&oldact,NULL);
  8 }
  9 int main(){
 10   struct sigaction sig;
 11   sig.sa_handler = sigcallback;
 12   sigaction(2,&sig,&oldact);
 13   while(1){
 14     printf("i am main func,sleep(1)\n");
 15     sleep(1);
 16   }
 17   return 0;
 18 }

结果如下:

第一次调用sigcallback函数时:ctrl+c被4.2.1的函数更改为废话函数,然后在这个废话函数里面又把sigcallback函数本身修改成为中断函数。 

8.信号的捕捉

介绍之前咱们先来回顾一下几个问题

1、进程的代码在用户空间还是内核空间?

struct  tast_struct { ... };内核空间

2、注册的原理:

操作系统修改tast_struct结构体。

3、程序有可能在用户空间执行,也有可能在内核空间执行

8.1.捕捉的过程

进程进入到内核空间之后才能处理信号。

进程从内核返回到用户态的时候,一定会调用do_signal处理信号

进程没有收到信号,直接返回到用户空间

进程收到信号了就进行默认处理、忽略处理以及自定义处理

8.2.信号的处理时机

当从内核态切换到用户态的时候会调用do_signal函数处理信号

-有,就处理信号

-没有,就直接返回用户态

8.3.处理信号的时候,不同的处理方式

默认、忽略:直接在内核当中就处理结束

自定义处理:调用程序员自己定义的处理函数进行处理

->执行用户自定义的处理函数(用户空间)

->调用sigreturn()再次回到操作系统内核(内核空间)

->再次调用会调用do_signal函数去处理信号

->调用sys_sigreturn函数回到内核空间,继续执行代码

9.信号的阻塞

9.1.要理解的点

信号的注册时信号注册,信号阻塞是信号阻塞。信号的阻塞并不会干扰信号的注册,而是说进程收到这个信号之后,由于阻塞,暂时不处理这个信号。

9.2.再次理解信号的处理时机

加入信号阻塞这个知识点之后,操作系统在处理信号时的处理时机又有了变化:

当从内核态切换到用户态的时候会调用do_signal函数处理信号

-有,就处理信号

判断信号是否被阻塞

-是,暂时不处理

-不是:直接处理

-没有,就直接返回用户态

9.3.接口

int  sigprocmask(int  how , const  sigset_t  *set , sigset_t  *oldset);

参数:

     how:想让sigprocmask做什么事情

SIG_BLOCK:设置某个信号为阻塞状态

当how为这个状态的时候,函数会根据set计算新的阻塞位图,方式为:block(new) = block(old) | set;

SIG_UNBLOCK:设置某个信号为为非阻塞状态

当how为这个状态的时候,函数会根据set计算新的阻塞位图,方式为:block(new) = block(old) & (~set);

SIG_SETMASK:第二个参数“set”,替换原来的阻塞位图。(替换)

当how为这个状态的时候,函数会根据set计算新的阻塞位图,方式为:block(new) = set;

     set:新设置的阻塞位图

     oldset:原来老的阻塞位图

int  sigemptyset(sigset_t  *set);         //清空位图,将比特位全部置为0

int  sigfillset(sigset_t  *set);               //将比特位全部置为1

int  sigaddset(sigset_t  *set , int  signum);     //将某个信号的对应比特位置为1

int  sigdelset(sigset_t  *set , int  signum);      //将某个信号的对应比特位置为0

int  sigismember(const  sigset_t  *set , int  signum);     //判断某个信号的位图位置是否为1

咱们来写一段代码印证阻塞位图的设置,代码如下:

  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<signal.h>
  4 int main(){
  5   sigset_t set;
  6   sigemptyset(&set);
  7   sigaddset(&set,2);
  8   sigprocmask(SIG_BLOCK,&set,NULL);
  9   while(1){
 10     printf("i am main func,sleep(1)\n");
 11     sleep(1);                                                         
 12   }
 13   return 0;
 14 }

运行结果:

可以明显看出^C已经被阻塞掉了,暂不处理所以无法让进程停止下来。 

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值