linux信号---信号的产生

linux信号---信号的产生

吐舌头信号的本质

   操作系统给进程发送信号,本质上是给进程的PCB中写入数据,修改相应的PCB字段,进程在合适的时间去处理所接受的信号。我们先从熟悉的场景说起:

1)用户输入一个命令,在shell下启动一个前台进程。

2)用户按下Ctrl-C,通过键盘输入产生了一个硬件中断。

3)如果CPU当前正在运行此进程的代码,则该进程的用户空间代码暂停执行,CPU从用户态切换到内核态处理中断。

4)终端驱动程序将Ctrl-C解释为一个SIGINT信号,记在该进程的PCB中。

5)当某个时刻从内核返回该进程的用户空间代码继续执行之前,首先处理PCB中记录的信号。SIGINT信号默认处理动作为终止信号,所以直接终止进程而不再返回到它的用户空间代码。

※❤ Ctrl+C所产生的信号只能发送给前台进程,如果想让其在后台运行,需要在命令后面加上&。这样shell不必等待进程结束就可以接受新的命令,启动新的进程。

Shell可以同时运行一个前台进程和任意多个后台进程,只有前台进程才能接受到诸如Ctrl-C这样的信号,前台进程在运行过程中用户随时按下Ctrl-C而产生一个信号,也就是说该进程的用户空间代码执行到任何地方都可能受到SIGINT信号而被终止,因此信号相对于进程的控制流来说是异步的。

吐舌头普通信号与实时信号

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



实时信号: 编号为34-64,它们与常规信号有很大的不同,因为它们必须排序以便发送多个信号能被接收到。但是同种信号的常规信号并不排序,尽管Linux内核并使用实时信号,它还是通过几个特定的系统调用完全实现了POSIX标准。

linux中采用位图存储

产生信号的条件:

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

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

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

信号处理方式:

进程一般通过3种方式处理信号

1,忽略此信号

2,执行该信号的默认处理动作

3,提供一个信号处理函数,要求内核在处理该信号时切换到用户态执行这个处理函数,这种方式称为捕捉一个信号,这个函数称为信号捕捉函数


typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
参数signum:信号的编号。

参数handler:是一个函数指针,sighandler_t类型的函数指针,表示接受此信号要执行的函数的地址。也可以是  SIG_ING 代表忽略SIGINT信号 SIG_DFL代表执行系统默认操作

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

 ----->对2号信号进行捕捉:Ctrl-C产生的SIGINT信号
  1 #include<stdio.h>
  2 #include<signal.h>
  3 void myhandle()
  4 {
  5 printf("hello word !\n");
  6 }
  7 
  8 int main(){
  9  signal(2,myhandle);
 10  while(1);
 11  return 0;
 12 }                                                                                                                                                      
          
此时Ctrl-C是不能终止程序的,无奈的我们只能用9号信号来杀死该进程。

接下来我们对此程序进行改进:

  1 #include<stdio.h>
  2 #include<signal.h>
  3 #include<unistd.h>                                                                                                                                     
  4 typedef void (*sighandler_t) (int);
  5 sighandler_t _myhandle=NULL;
  6 void myhandle()
  7 {
  8 printf("hello word !\n");
  9 signal(2,_myhandle);
 10 }
 11 
 12 int main(){
 13 _myhandle = signal(2,myhandle);
 14  while(1);
 15  return 0;
 16 }

首先我们用Ctrl-C捕捉2号信号,并用_handler函数指针对象接受,在myhandler函数内,再次用Ctrl-C捕捉2号信号,并指向_handler捕捉成功,返回之前的信号处理函数,即恢复了默认处理,程序得以终止。

产生信号的方式:

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

2)调用系统函数向进程发送信号。

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

#include <signal.h> 
int kill(pid_t pid, int signum); //给任意进程发送任意信号
int raise(int signo); //给自己发送任意信号

参数pid:进程号。

参数signum:信号的编号。

这两个函数都是成功返回0,错误返回1.

abort可以使当前进程接收到信号而异常终止,但是abort会认为进程不安全。

3)由软件条件产生信号

进程可以通过调用alam向它自己发送SIGALRM信号,其函数原型如下:

#include <unistd.h>
unsigned int alam(unsigned int secs);

参数secs:alarm函数安排内核在secs秒内发送一个SIGALRM信号给调用进程。如果secs等于0,那么不会调度新的闹钟(alarm)。

返回值:前一次闹钟剩余的秒数,若以前没有设定闹钟,则为0。





猜你喜欢

转载自blog.csdn.net/sx2448826571/article/details/79808310