Linux内核中的信号(一)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/rengui1228/article/details/72901241

一、Linux信号的基本概念

1、什么是信号?

       软中断信号(signal,又简称为信号)用来通知进程发生了异步事件。在软件层次上是对中断机制的一种模拟,在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。信号是进程间通信机制中唯一的异步通信机制一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知道信号到底什么时候到达。进程之间可以互相通过系统调用kill发送软中断信号。内核也可以因为内部事件而给进程发送信号,通知进程发生了某个事件。信号机制除了基本通知功能外,还可以传递附加信息

      简单地说,马路上的红绿灯是一种信号,它能提示我们怎样安全的过马路。信号就是当你看到它是知道它是什么,并且知道看到信号之后应该做什么,至于你遵不遵守就是你自己的事了, 计算机中的信号也不例外。


2、为了理解信号,先从我们最熟悉的场景说起:

     1. ⽤用户输⼊入命令,在Shell下启动⼀一个前台进程。
     2. ⽤用户按下Ctrl-C,这个键盘输⼊入产⽣生⼀一个硬件中断。
     3. 如果CPU当前正在执⾏行这个进程的代码,则该进程的⽤用户空间代码暂停执⾏行,CPU从⽤用户态 切换到内核态处理硬件中断。
     4. 终端驱动程序将Ctrl-C解释成⼀一个SIGINT信号,记在该进程的PCB中(也可以说发送了⼀个SIGINT信号给该进程)。
     5. 当某个时刻要从内核返回到该进程的⽤用户空间代码继续执⾏行之前,⾸首先处理PCB中记录的信号,发现有⼀个SIGINT信号待处理,⽽而这个信号的默认处理动作是终⽌进程,所以直接终⽌止进程⽽而不再返回它的⽤用户空间代码执⾏行。

     注意,Ctrl-C产⽣生的信号只能发给前台进程。⼀个命令 后⾯面加个&可以放到后台运⾏行,这样Shell不必等待进结束就可以接受新的命令,启动新的进程。Shell可以同时运⾏行⼀个前台进程和任意多个后台进程,只有前台进程才能接到像Ctrl-C这种控 制键产⽣生的信号。前台进程在运⾏行过程中⽤用户随时可能按下Ctrl-C⽽而产⽣生⼀一个信号,也就是说该进 程的⽤用户空间代码执⾏到任何地⽅方都有可能收到SIGINT信号⽽而终⽌止,所以信号相对于进程的控制流 程来说是异步(Asynchronous)的。
     ⽤用kill -l命令可以察看系统定义的信号列表:(普通信号、实时信号)



注意列表中不是64个信号而是62个信号。
1-31:普通信号
34-64:实时信号

注意:普通信号是采用“位图”的存储方式,只需要4个字节,32个比特位就可以表示了。其中0表示没有改信号,1表示收到了改信号。

每个信号都有⼀一个编号和⼀一个宏定义名称,这些宏定义可以在signal.h中找到,例如其中有定 义#define SIGINT 2。编号34以上的是实时信号,本章只讨论编号34以下的信号,不讨
论实时信号。这些信号各⾃自在什么条件下产⽣生,默认的处理动作是什么,在signal(7)中都有详细说明:   man 7 signal


二、信号的产生及处理


1、产⽣生信号的条件主要有:

   1). ⽤用户在终端按下某些键时,终端驱动程序会发送信号给前台进程,例如Ctrl-C产⽣生SIGINT信号,Ctrl-\产⽣生SIGQUIT信号,Ctrl-Z产⽣生SIGTSTP信号。
   2). 硬件异常产⽣生信号,这些条件由硬件检测到并通知内核,然后内核向当前进程发送适当的信号。例如当前进程执⾏行了除以0的指令,CPU的运算单元会产⽣生异常,内核将这个异常解释 为SIGFPE信号发送给进程。再⽐比如当前进程访问了⾮非法内存地址,,MMU会产⽣生异常,内核 将这个异常解释为SIGSEGV信号发送给进程。
   3). ⼀个进程调⽤用kill(2)函数可以发送信号给另⼀一个进程。 可以⽤用kill(1)命令发送信号给某个进程,kill(1)命令也是调⽤用kill(2)函数实现的,如果不明确指定信号则发SIGTERM信号,该信号的默认处理动作是终⽌止进程。 当内核检测到某种软件条件发⽣生时也可以通过信号通知进程,例如闹钟超时产⽣生SIGALRM信号,向读端已关闭的管道写数据时产⽣生SIGPIPE信号。 如果不想按默认动作处理信号,⽤用户程序可以调⽤用sigaction(2)函数告诉内核如何处理某种信号,可选的处理动作有以下三种:
   a. 忽略此信号。
   b. 执⾏行该信号的默认处理动作。
   c. 提供⼀一个信号处理函数,要求内核在处理该信号时切换到⽤用户态执⾏行这个处理函数,这种方 式称为捕捉(Catch)⼀个信号。

2、产生信号的方式

   1).通过终端按键产生信号:SIGINT(Ctrl-c)默认处理动作是终止前台进程;SIGQUIT(Ctrl-\)默认处理动作是终止进程(包括前台进程,一般用来结束不正常的程序)。

   2).通过系统函数向进程发信号。

3、处理信号的方法

   1).忽略此信号(大多数信号都可以使用该方法处理)。
         特例:SIGKILL SIGSTOP
         原因:它们向超级用户提供一种使进程终止或停止的方法,同时,如果忽略某些由硬件异常产生的信号,则进程的行为是未定义的。

   2).执行该信浩的默认处理动作(与信号的种类有关,大多数信号会直接终止该进程)。

   3).用信号捕捉函数为该信号指定自定义动作。
         特例:不能捕捉SIGKILL SIGSTOP信号
         原因:为了防止非法用户的恶意入侵使得进程永远杀不掉。
         信号捕捉函数:修改信号的默认动作,有些信号是不能被捕捉的,如9号信号SIGKILL

#include <signal.h>

       typedef void (*sighandler_t)(int);

       sighandler_t signal(int signum, sighandler_t handler);
参数:signum:信号编号handler:指向怎样捕捉该信号的函数
返回值:signal函数的返回值是一个函数指针,成功返回返回以前的处理 配置,失败返回错误码对应的错误提示。

Makefile:


signal.c:

#include<unistd.h>
#include<stdio.h>
#include<signal.h>

void myhandler(int signo)
{
    printf("got SIGINT\n");
}
int main()
{
   signal(2,myhandler);
   while(1)
   {
       sleep(1);
   }
   return 0;
}

运行结果:

捕捉2号信号



用9号信号终止进程

修改signal.c

#include<unistd.h>
#include<stdio.h>
#include<signal.h>

typedef void (*sighandler_t)(int);
sighandler_t old_handler = NULL;

void myhandler(int signo)
{
      printf("got SIGINT\n");
      signal(2, old_handler);
}
int main()
{
   old_handler = signal(2,myhandler);
   while(1)
   {
       sleep(1);
   }
   return 0;
}

运行结果:

这就是我的关于Linux信号(一)后续还有我对Linux信号(二)的解读······




展开阅读全文

没有更多推荐了,返回首页