Linux-信号

信号

信号概念

信号是为了操作系统能控制进程正确的运行而产生的一种机制。它分为俩种类型,第一种是普通信号(标准信号),第二种是实时信号。通常进程产生普通信号有四种方式:
1 由键盘特殊的组合按键产生,该方式产生的信号发送给一个控制终端的前台作业。
2 由系统调用接口或者命令发出一个信号。(列如kill 命令,abort—异常终止信号)
3 由进程自身错误操作产生的异常导致的信号
4 软件条件产生的信号(列如 alarm , 还有管道中的SIGPIPE)
5 硬件产生 SIGBUS 和 却页中断

普通信号(Standard Signals)

Linux下普通信号为1~32。它由pending表/block表/handler表来管理。当有多个非同类型的普通信号产生时,信号抵达顺序是未定义的。有多个同类型的信号产生的时候,pending表只记录一个。还有一个就是Linux下的普通信号都是可靠信号。(这个意思就是多个同种类型信号产生的时候,系统会阻塞该信号,最终产生完毕后,接触阻塞最终只有一个信号产生)

实时信号(Run-time)

实时信号都是未定义的信号,在用户定义之前。它的抵达是有序抵达,以数字小的为最高优先级。当有多个同类型的信号产生时,按顺序抵达。使用应配合宏来一起使用SIGRTMIN+n ,但是不能超过 SIGRTMAX 。它的范围是64-33,刚好32个。使用宏的原因是,Linuxthreads线程库使用了3个实时信号,posix线程库使用了2个。我们用宏编译器会自动转换的。
当实时信号和普通信号同时产生时,Linux以先处理普通信号。

被信号中止的系统调用和库函数

epoll类的系统调用,system v 的系统调用,select poll epoll,已经设置了超时的网络系统调用的函数等系统调用被中止后返回 ENTER 错误。
read, readv, write, writev, ioctl,wait,waitpid等函数被中断后,如果由signal函数注册的自定义函数/sigaction函数设置了 SA_RESTART 标志的话,这些被中断的系统调用会自动重启。

信号的处理方式
信号一般由产生到未决再到递达。
产生:系统在该进程pcb的信号字段修改该进程的pending表中相应的有效位。
未决:pending表中有未处理的信号,该进程还没有对相应的信号进行处理。
递达:已经处理了相应的信号。递达有三种方式,忽略SIG_DFL,默认动作SIG_IGN,自定义handler的一个函数指针。
忽略也是递达的一种方式,如果信号被阻塞永远不会被递达。
信号阻塞与SIG_IGN

如果一个信号被阻塞,那么这个信号还是会在Pending表中,说明这个信号产生了,但是现在没办法处理。而SIG_IGN则是表示忽略,它也是信号递达(信号处理)的一种方式,如果该信号设置了SIG_IGN,那么它就会从Pending表中消失,表示已经处理过了。虽然它们俩个现象都是什么都不处理,但是本质是不同的。

下图是我另一篇博客中关于task_struct的信号字段的截图

这里写图片描述

进程中保存信号的数据结构

这里写图片描述
信号在pcb中的三个数据结构。

  • block表:它又称信号屏蔽字,这个表中的有效位表示,即使信号处于未决状态也不能递达,该信号处于屏蔽状态
  • pending表:它又称未决表,该表记录了进程未处理的信号。它的有效位表示该信号进程已收到但还未处理。
  • handler表:表示信号的处理方式。
代码验证pending表与block表
#include <stdio.h>
#include <signal.h>
void handler(int signum)
{
  printf("ctrl-c ----> SIGINT\n");
}
void printsigset(sigset_t * set)
{   
    size_t idx=0;
    for(idx=0;idx<32;idx++)
    {  
       if(0==sigismember(set,idx))
        putchar('0');
       else
        putchar('1');
    }
    putchar('\n');
}
int main()
{
  struct sigaction act,oact;
  sigset_t mask,oldmask,pending;
  act.sa_handler=handler;
  act.sa_flags=0;
  sigemptyset(&act.sa_mask);
  sigemptyset(&mask);
  sigemptyset(&oldmask);
  sigemptyset(&pending);
  sigaddset(&mask,SIGINT);
  sigprocmask(SIG_BLOCK,&mask,&oldmask);
  sigaction(SIGINT,&act,&oact);
  int second=0;
   while(second++<5)
   {   
     sigpending(&pending);
     printf("pending table:\n");
     printsigset(&pending);
     sleep(1); 
     printf("block table:\n");
     printsigset(&mask);
   }   
   sigsuspend(&oldmask);
    printf("ok.. Now recovered block table\n");
   sigprocmask(SIG_SETMASK,&oldmask,NULL);
   sigpending(&pending);
   printf("Now pending table\n");
   printsigset(&pending);
   sigaction(SIGINT,&oact,NULL);
   return 0;
}

这里写图片描述

signal 与 sigaction api 区别

它们俩个区别在于通过signal API设置的信号,在handler执行完毕后,该信号会恢复默认行为,而sigaction则不会。

特殊的三个信号

SIGKILL

不能被阻塞、忽略、捕捉。

SIGSTOP

不能被阻塞、忽略、捕捉。

SIGABRT

不能被阻塞、忽略,但是可以被捕捉,虽然可以捕捉,但是在执行完自定义函数后进程还是会被终止。唯一的区别,如果捕捉,会自动在自定义函数中调用 fclose(NULL)然后关闭所有I/O流的同时刷新I/O流缓冲区。

SIGCHD 语义

  默认SIGCHID信号是忽略的,但是如果我们手动调用sigaction 或者 signal函数置它为SIG_IGN 也就是主动置它为忽略式的处理方式,那么根据SIGCHD语义将不会产生僵尸进程,内核并不会为我们保留子进程终止时的信息

进程收到一个信号的处理时机与流程

一个进程收到信号不会立即去处理,除非在发送用户态到内核态的切换时才会触发信号处理。

信号表与线程

新产生的线程的pending表会被清除,handle与block表继承自主线程。

信号处理机制

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值