linux的信号详解

    • 信号概念
      • 定义:
      • 信号产生场景:
      • 信号分类:
      • 进程收到信号的三种处理方式:
      • 信号列表:
      • 系统信号详解:
    • 操作信号
      • 使用命令发送信号给进程
      • 给本进程发信号:
      • 调改变信号处置:
        • 信号处理器程序介绍
        • 示例代码:

信号概念

定义:

 信号是事件发生时对进程的通知机制。

信号产生场景:

  • 键盘事件
  • 非法内存操作
  • 硬件故障
  • 从用户态切换到内核态

信号分类:

  • 标准信号
    标准信号的局限性:

    1. 阻塞信号可能会丢失。当一个信号阻塞时,这个信号即使多次发送给进程,也被执行一次信号句柄。

    2. 信号交付没有携带与信号有关信息。接受到信号的进程无法区分同种信号的不同情况,也不知道信号从何而来。

    3. 信号的交付没有优先级。当有多个信号悬挂与一个进程时,交付的顺序不确定。

  • 实时信号
    实时信号对标准信号做了一下扩充,有以下的特点:

    1. 增加了信号从SIGRTMIN到SIGRTMAX的实时信号,可以通过sysconf(_SC_RTSIG_MAX)获得当前操作系统支持的实时信号的个数。

    2. 实时信号在队列中并按顺序交付。同一类型的实时信号将按顺序交付给进程。

    3. 实时信号可以携带额外的信息。

    4. 进程能够通过专门的函数更快的回复信号。

    5. 当定时器到期、空消息队列有消息到达、有异步IO完成时,信号能够及时交付给进程。

进程收到信号的三种处理方式:

1.默认处理:

  • 忽略信号:内核将信号丢弃,信号没有对进程产生任何影响
  • 终止进程:进程异常终止
  • 产生核心转储文件,同时终止文件
  • 停止进程:暂停进程的执行
  • 恢复之前暂停的进程继续执行

2.忽略处理:

  • 信号来了不做任何处理不能忽略SIGKILL和SIGSTOP

3.捕获并处理:

  • 信号来了捕获信号,并执行程序员自己写的程序不能捕获SIGKILL和SIGSTOP

信号列表:

linux的信号详解_第1张图片
列表中,编号为1 ~ 31的信号为传统UNIX支持的信号,是不可靠信号(非实时的),编号为32 ~ 63的信号是后来扩充的,称做可靠信号(实时信号)。不可靠信号和可靠信号的区别在于前者不支持排队,可能会造成信号丢失,而后者不会。

系统信号详解:

  1. SIGHUP
     当终端断开时,将发送该信号给终端控制进程。SIGHUP信号还可用于守护进程。
  2. SIGINT
     当用户键入终端中断字符(如:Ctrl + C)终端驱动程序将发送该信号给前台进程组。该信号默认行为是终止进程。
  3. SIGQUIT
     当用户在键盘键入退出字符(如Ctrl+\)时,该信号将发往前台进程组。默认情况下,该信号终止进程,并生成可用于调试的核心转储文件。当进程陷入无限循环或者不在响应,使用该信号很合适。
  4. SIGILL
     进程试图非法执行机器语言指令,系统将向该进程发送该信号。
  5. SIGTRAP
     该信号用来实现断点调试功能以及strace命令所执行的跟踪系统调用功能。
  6. SIGABRT
     当进程调用abort函数时,系统向该进程发送该信号。默认情况下,该信号会终止进程,并产生核心转储文件。
  7. SIGBUS
     总线错误,表示发生了某种内存访问错误。当使用mmap()所创建的内存映射时,如果试图访问的地址超出了底层内存映射文件的结尾,会产生该错误。
  8. SIGFPE
     在发生致命的算术运算错误时发出. 不仅包括浮点运算错误, 还包括溢出及除数为0等其它所有的算术的错误。
  9. SIGKILL
     此信号为必杀信号,处理器程序无法阻塞、忽略或者捕获,故而总能杀死进程(僵尸进程除外)。
  10. SIGUSR1
     用户自定义信号,内核绝不会为进程产生该信号。
  11. SIGSEGV
     试图访问未分配给自己的内存, 或试图往没有写权限的内存地址写数据.
  12. SIGUSR2
     同SIGUSR1描述。
  13. SIGPIPE
     当某一进程试图向管道,FIFO或套接字写入信息时,如果这些设备并无相应的阅读进程,系统将产生该信号。(管道破裂)
  14. SIGALRM
     经调用alarm()或setitimer()而设置的实时定时器一旦到期,内核将产生该信号。
  15. SIGTERM
     这是用来终止进程的标准信号,也是kill和killall命令所发送的默认信号。用户经常会使用kil -9显示向进程发送SIGKILL信号,然而这一做法通常是错误的。精心设计的应用程序应当为SIGTERM信号设置处理器程序,以便于其能够预先清理临时文件和释放资源,做到全身而退。发送SIGKILL信号可以杀掉某个进程,从而绕开了SIGTERM的信号处理程序。因此,总是应该首先尝试使用SIGTERM信号来终止进程,而把SIGKILL信号作为最后手段,去对付那些失控的进程。
  16. SIGSTKFLT
     linux对该信号做了定义,但并未加以使用。
  17. SIGCHLD
     当父进程的某一子进程退出时,内核将向父进程发送该信号。
  18. SIGCONT
     该信号发送给已停止的进程,进程将恢复运行。当接收信号的进程当前处于非停止状态 时,默认情况下将忽略该信号。
  19. SIFSTOP
     进程收到该信号将停止运行,处理器程序无法将其阻塞、忽略或者捕获,故而总能停止进程。
  20. SIGTSTP
     作业控制的停止信号,当用户在键盘输入挂起字符(如:Ctrl+Z)时,将发送该信号给前台进程组,使其停止运行。(该信号可以被处理和忽略)
  21. SIGTTIN
     在作业控制shell下运行时,若后台进程组试图对终端进行read()操作,终端驱动程序则将该进程组发送该信号。该信号默认将停止进程。
  22. SIGTTOU
     该信号与SIGTTIN类似,但在写终端(或修改终端模式)时收到。
  23. SIGURG
     系统发送该信号给一个进程,表示套接字上存在带外(紧急)数据。
  24. SIGXCPU
     当进程的CPU时间超出对应的资源限制时,将发送此信号给进程。
  25. SIGXFSZ
     如果进程试图增大文件而突破对进程文件大小的资源限制时,将发送该信号给进程。
  26. SIGVTALRM
     虚拟时钟信号. 类似于SIGALRM, 但是计算的是该进程占用的CPU时间。
  27. SIGPROF
     类似于SIGALRM/SIGVTALRM, 但包括该进程用的CPU时间以及系统调用的时间.
  28. SIGWINCH
     窗口大小改变时发出该信号。
  29. SIGIO
     文件描述符准备就绪, 可以开始进行输入/输出操作。
  30. SIGPWR
     电源故障信号。
  31. SIGSYS
     如果进程发起的系统调用有误,将产生该信号。

操作信号

使用命令发送信号给进程

kill -信号 进程id
kill命令是使用kill函数实现的。

#include 
#include 

       int kill(pid_t pid, int sig);

pid:指定哪个进程

  • pid>0:会发送信号给指定进程号为pid的进程。
  • pid=0:发送信号给调用进程组同组的每个进程,包括调用进程本身。
  • pid=-1:调用进程有权将信号发往的每一个进程,除去init(1号进程)和调用进程自身。如果特权级进程发起这一调用,那么会将信号发给系统中所有进程。有时也称这种信号称为广播信号。
  • pid<-1:会向该pid绝对值的进程组下属进程发送信号。
    sig:发送哪个信号

kill还有一个特殊的用途:当sig=0时,表示无信号发送,调用kill()函数会去执行错误检查,查看是否可以向目标进程发送信号。这也就意味着可以指定空信号来检测ID进程是否存在。

给本进程发信号:

 #include 
       int raise(int sig);

调改变信号处置:

系统调用函数

 #include 

       typedef void (*sighandler_t)(int);

       sighandler_t signal(int signum, sighandler_t handler);
参数:
signum:表示希望修改的信号编号
handler:表示信号到达时执行的处理器程序的地址,该函数无返回值。
  1. 自定义函数
  2.SIG_DFL:将信号处置重置为默认值。这适用于将之前signal()调用所改变的信号处置还原。
  3.SIG_IGN:忽略该信号。

handler一般格式:

void handler(int sig)
{
    //处理程序
}
返回值:
成功:signal()的返回值是之前的信号处置,像handler参数一样,是一个指针,指向的是带有一个整形参数且无返回值的函数。
失败:返回SIG_ERR

信号处理器程序介绍

 信号处理器程序时当指定信号传递给进程时将会调用的一个函数。
调用信号处理程序,可能会随时打断主程序流程,内核代表进程来调用处理器程序,当处理器程序返回时,主程序会在打断的位置处恢复执行。
linux的信号详解_第2张图片
信号处理器程序设计原则:力求简单。

示例代码:

void handler(int sig) //处理器程序
{
    printf("ctrl + c\n");
}

int main()
{
    int sig = SIGINT; //信号
    if(signal(sig,handler) == SIG_ERR)
        perror("signal"),exit(1);

    int i = 0;
    while(1)
    {   
        printf("%c ",i%26+'a');
        fflush(stdout);
        i++;
        sleep(1);
    }                                                                                                                                
    return 0;
}

执行结果:
linux的信号详解_第3张图片
当键入Ctrl+C时,会去执行信号处理程序,进程不会结束,执行完毕再返回继续执行主程序,最后发送SIGQUIT(Ctrl + \)信号,默认执行核心转储,进程退出。

你可能感兴趣的:(linux)