Linux信号机制概述


还是先看看Linux中用户空间怎么运用的,用户空间编程实例如下:

#include<signal.h>

#include<stdio.h>

#include<unistd.h>

/*下面为两个新的信号操作函数*/

void handler(int sig)

{

         printf("Receive signal :%u\n",sig);

}

void sigroutine(int num)

{

         switch(num)

         {

         case 1:

                   printf("SIGUP signal\n");

                   break;

         case 2:

                   printf("SIGINT signal\n");

                   break;

         case 3:

                   printf("SIGQUIT signal\n");

                   break;

         default:

                   break;

         }

         return;

}

int main(void)

{

         struct sigaction sa;

         int count;

         sa.sa_handler=handler;

         sigemptyset(&sa.sa_mask);

         sa.sa_flags=0;

         printf("task id is:%d\n",getpid());

/*下面四条语句为相应的信号设置新的处理方法*/

         sigaction(SIGTERM,&sa,NULL);

         signal(SIGHUP,sigroutine);

         signal(SIGINT,sigroutine);

         signal(SIGQUIT,sigroutine);

 

         while(1)

         {

                   sigsuspend(&sa.sa_mask);/*阻塞,一直等待信号到达*/

                   printf("loop\n");

         }

         return 0;

}

可见,用户空间调用了很多系统调用来实现信号的编程,为了弄清楚他的内在原理,决定将内核中的实现做一个大致的梳理。为了理清思路,我们由内核中实现信号操作涉及的关键数据结构关系画出下图,我们看到,内核中的数据结构实现较简单,主要分两部分,一部分用于信号操作(即handler),由进程的sighand字段开始;另一部分用于信号的挂起,由进程的signal和pending字段索引。

由关系图,我们大致观其实现原理如下:

1,   进程的所有信号(现为32个)由一个数组task->sighand->action[]保存,数组的下标即为信号的ID,比如SIGQUIT等,每个操作由一个数据结构sigaction实现,该字段的sa_handler即为实现的操作;

2,   进程对挂起的信号有两种队列,一种为所有进程共享的。该队列的每一项为一个sigqueue结构,通过该结构info字段的si_signo等属性可以定位到对应的信号ID。其中sigset_t结构为一个32位整型,用于定位到ID,即类似位图的表示。

我们看几个最基本的操作于内核中的实现。

  • 1
  • 2
  • 3
  • 下一页

相关内容