欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

信号初识

程序员文章站 2022-03-19 13:16:55
...

信号(linux系统为了响应某些状况而产生的某些事件,进程收到信号应该采用相应的动作)

------->认识信号

信号在最在的Unix系统中即被引入,用于用户态进程间通信,内核也可用信号通知进程系统所发生的时间。在现实生活中,我们每天都在接触信号,下课铃声、红绿灯、闹钟等都是信号。

操作系统给进程发送信号,本质上是给进程的PCB中写入数据,修改相应的PCB字段,进程在合适的时间去处理所接受的信号。

A.信号常见的场景

a.用户输入命令,在Shell下启动一个前台进程

b.用户按下Ctrl+C,这个键盘输入产生一个硬件中断

c.如果CPU当前正在执行这个进程的代码,则该进程的用户空间代码暂停执行,CPU从用户态切换到内核态处理硬件中断

d.终端驱动程序将Ctrl-C解释成一个SIGINT信号,记在该进程的PCB中(也可以说发送了一个SIGINT信号给该进程)

e.当某个时刻要从内核返回到该进程的用户空间代码继续执行之前,首先处理PCB中记录的信号,发现有一个SIGINT信号待处理,而这个信号的默认处理动作是终止进程,所以直接终止进程而不再返回它的用户空间代码执行.

信号初识信号初识1. Ctrl-C产生的信号只能发给前台进程。一个命令 后面加个&可以放到后台运行,这样Shell不必等待进程结束就可以接受新的命令,启动新的进程。
2. Shell可以同时运行一个前台进程和任意多个后台进程,只有前台进程才能接到像Ctrl-C这种控制键产生的信号。

3. 前台进程在运行过程中用户随时可能按下Ctrl-C而产生一个信号,也就是说该进程的用户空间代码执行到任何地方都有可能收到SIGINT信号而终止,所以信号相对于进程的控制流程来说是异步(Asynchronous)的。

R为后台进程S+/R+为前台进程

系统定义的信号列表:

我们使用kill -l命令可以查看系统定义的信号列表,每个编号都有一个宏与之对应,可以在/usr/include/asm/signal.h中查看,下图中1-31为普通信号(不可靠信号)(非实时信号),34-64为实时信号(可靠信号)。

[aaa@qq.com sharememry]$ kill -l
 1) SIGHUP	 2) SIGINT	 3) SIGQUIT	 4) SIGILL	 5) SIGTRAP
 6) SIGABRT	 7) SIGBUS	 8) SIGFPE	 9) SIGKILL	10) SIGUSR1
11) SIGSEGV	12) SIGUSR2	13) SIGPIPE	14) SIGALRM	15) SIGTERM
16) SIGSTKFLT	17) SIGCHLD	18) SIGCONT	19) SIGSTOP	20) SIGTSTP
21) SIGTTIN	22) SIGTTOU	23) SIGURG	24) SIGXCPU	25) SIGXFSZ
26) SIGVTALRM	27) SIGPROF	28) SIGWINCH	29) SIGIO	30) SIGPWR
31) SIGSYS	34) SIGRTMIN	35) SIGRTMIN+1	36) SIGRTMIN+2	37) SIGRTMIN+3
38) SIGRTMIN+4	39) SIGRTMIN+5	40) SIGRTMIN+6	41) SIGRTMIN+7	42) SIGRTMIN+8
43) SIGRTMIN+9	44) SIGRTMIN+10	45) SIGRTMIN+11	46) SIGRTMIN+12	47) SIGRTMIN+13
48) SIGRTMIN+14	49) SIGRTMIN+15	50) SIGRTMAX-14	51) SIGRTMAX-13	52) SIGRTMAX-12
53) SIGRTMAX-11	54) SIGRTMAX-10	55) SIGRTMAX-9	56) SIGRTMAX-8	57) SIGRTMAX-7
58) SIGRTMAX-6	59) SIGRTMAX-5	60) SIGRTMAX-4	61) SIGRTMAX-3	62) SIGRTMAX-2
63) SIGRTMAX-1	64) SIGRTMAX	

信号初识bit位的位置表示对于信号的编号,用0来表示未接受到信号,1表示接受到信号。存储这32位信号的空间恰好需要4个字节,因此采用位图存储是最好不过的.

B.信号处理常见的方式

   a.忽略此信号(挂电话) SIG_IGN

   b.执行该信号的默认处理动作(不管不处理)SIG_DFL

   c.捕捉信号(接电话)

信号是异步事件, 当信号到达,保存当前进程的执行环境,转去执行信号处理函数,当信号处理函数执行完毕,恢复现场,继续执行

C.产生信号的主要条件 

1)用户在终端按下某些键时,终端驱动会发送信号给前台进程,例如Ctrl-C产生的SIGINT信号、Ctrl-\产生的SIGQUIT信号、Ctrl-Z产生的SIGTSTP信号。

2)硬件异常产生的信号,这些条件由硬件检测并通知内核,然后内核向当前进程发送适当的信号。比如当前进程访问了非法内存地址,MMU(内存管理单元)会产生异常,内核将这个异常解释为SIGSEGV信号发送给进程。

3)一个进程调用kill(2)函数可以发送信号给另一个进程,可以调用kill(1)命令发送信号给某个进程,kill(1)命令也是调用kill(2)函数实现的。如果不明确指定信号,则发送SIGTERM信号,该信号的默认处理动作是终止进程,当内核检测到软件条件发生时可以通过信号通知进程。

D.信号捕捉函数

#include<signal.h>
ypedef void (*sighandler_t)(int);
sighandler_t signal(int signum,sighandler_t handler);

参数signum:信号的编号。

参数handler:是一个函数指针,表示接受此信号要执行的函数的地址。

返回值:若成功则为指向前次处理程序的指针,若出错则为SIG_ERR

代码演示

  1 #include<stdio.h> 
  2 #include<unistd.h>
  3 #include<signal.h>
  4 #include<stdlib.h>
  5 void handler(int s){
  6  printf("recv %d\n",s);
  7 }
  8 int main(void){
  9  __sighandler_t ret;
 10  ret = signal(SIGINT,handler);
 11  if(ret==SIG_ERR){
 12  perror("signal");
 13  exit(1);
 14  }
 15  while(getchar()!='\n');
 16  if(signal(SIGINT,ret)==SIG_ERR){
 17  perror("signal");
 18  exit(1);
 19  }
 20  while(1){
 21  printf(".");
 22  fflush(stdout);
 23  sleep(1);
 24  }
 25 }

信号初识

D.产生信号的方法

a.通过终端按键产生信号Core dump)。

b.调用系统函数向进程发送信号。

kill命令是调用kill函数实现的,kill函数可以给一个特定的进程发送指定的信号。raise函数可以给当前进程发送指定的信号(自己也可以给自己发送信号)

信号初识对于kill函数 而言:①,pid>0信号发送给pid进程 ②.pid==0时,信号发送给调用所在的进程组的所有进程,③.pid=-1,信号发送给除了1号进程之外的任何一个,④.pid<-1,例如(-5),信号发送给5号进程组里的所有进程

    给自己发信号
        int raise(int signum)
    给进程组发送信号
        int killpg(int gid, int signum);

    暂停进程,直到被信号打断

        int pause( void )

    使当前进程接收到信号而异常终止

        void abort(void);-------就像exit函数一样,abort函数总会成功的,所以没有返回值

进程组 : 进程组中有若干个进程

             用管道连接的进程, fork 创建出来的父子进程都属于同一个进程组

b.由软件条件产生信号。

 #include<unistd.h>

 unsigned int alarm(unsigned int second)

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

当sec规定的时间到了,触发SIGALRM信号 ,如果sec是0,表示清除信号

返回值为0或者是以前设定的闹钟还剩余的秒数.

函数原型-----代码演示

#include <signal.h> 
int kill(pid_t pid, int signum); //给任意进程发送任意信号
int raise(int signo); //给自己发送任意信号
  1 #include<stdio.h>                                                                                                                
  2 #include<stdlib.h>
  3 #include<unistd.h>
  4 #include<signal.h>
  5 void handler(int n){
  6   printf("recv %d\n",n);//进程接收信号时执行的函数
  7 }
  8 int main(){
  9  signal(SIGUSR1,handler);//注册信号
 10  pid_t pid = fork();//创建子进程
 11  if(pid==0){
 12  //子进程
 13  sleep(1);
 14  kill(getppid(),SIGUSR1);//每隔1秒给父进程发送信号
 15  exit(1);
 16 
 17  }
 18  else{
 19    while(1){
 20     printf(".");
 21     fflush(stdout);
 22     int n=3;
 23     do
 24         n=sleep(3);
 25     while(n>0);
 26    }
 27  
 28  }
 29 return 0
 30    }

信号初识