Linux —— 进程信号

一、信号的基本概念

1. 什么是信号

  信号是Linux系统为了响应某种状况而产生的事件。进程收到信号后应该采取相应的动作。
  
  信号机制是进程之间相互传递消息的一种方法,信号全称为软中断信号,也有人称作软中断。从它的命名可以看出,它的实质和使用很象中断。所以,信号可以说是进程控制的一部分


2. 哪些情况会产生信号?

  • 键盘事件
  • 非法内存
  • 硬件故障
  • 环境切换

3. 如何查看信号

  1. 查看系统中的信号: kill -l;
  2. 查看信号的默认处理: man 7 signal;
  3. 有的信号有多个值,对应不同系统(中间那个值对应Linux系统)
    这里写图片描述

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

  1. 默认:执⾏行该信号的默认处理动作。
  2. 忽略:信号来了不处理,相当于丢掉信号(SIGKILLSIGSTOP不能被忽略)
  3. 捕获并处理:提供一个信号处理函数,要求内核在处理该信号时切换到用户态执行这个函数(SIGKILLSIGSTOP 不能被捕获)。

5. 信号的分类

  1. 不可靠信号 (1-31号信号不可靠)
      Linux的信号继承自早期的UNIX信号,UNIX信号有一定的缺陷:
       1. 信号处理函数执行完毕,信号恢复成默认处理方式(Linux已改进)
       2. 会出现信号丢失,信号不排队
        
  2. 可靠信号(34-64号信号可靠)
      不会出现信号丢失,支持排队,信号处理函数执行完后不会恢复成缺省处理方式。

  3. 实时信号:就是可靠信号

  4. 非实时信号:就是不可靠信号 

可靠信号和不可靠信号的区别:

  • 这里的不可靠主要是不支持信号队列,就是当多个信号发生在进程中的时候(收到信号的速度超过进程处理的速度的时候),这些没来的及处理的信号就会被丢掉,仅仅留下一个信号。

  • 可靠信号是多个信号发送到进程的时候(收到信号的速度超过进程处理信号的速度的时候),这些没来的及处理的信号就会排入进程的队列。等进程有机会来处理的时候,依次再处理,信号不丢失。


二、操作信号

1. 注册信号 —— signal()函数

signal()函数详解

  注册信号实际是对信号进行三种处理操作,用于告诉当前进程接收信号后该去执行什么动作。

#include <signal.h>

typedef void (*sighandler_t)(int); 
sighandler_t signal(int signum, sighandler_t handler); 

函数功能:开始获取信号值为signum的信号,如果获取到该信号,则开始执行handler指向的函数。

第一个参数signum:指明了所要处理的信号类型,它可以取除了SIGKILLSIGSTOP外的任何一种信号。

第二个参数handler:描述了与信号关联的动作,它可以取以下三种值:

  1. SIG_IGN:表示忽略该信号。 #define SIG_IGN ((sighandler_t)1)

  2. SIG_DFL:表示恢复对信号的系统默认处理。不写此处理函数默认也是执行系统默认操作。#define SIG_IGN ((sighandler_t)0)

  3. sighandler_t类型的函数指针:执行自己写的代码。

返回值:
  成功返回原本的信号处理函数指针;
  失败返回SIGERR; SIGERR的宏为 #define SIG_IGN ((sighandler_t)-1)
  

代码示例:

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

void handler(int n)
{
    printf("叫你装逼, 这下死了吧!\n");
    exit(0);
}

int main()
{
    signal(SIGINT, SIG_IGN);
    //SIGINT代表 ctrl+c 发起的信号,SIG_IGN表示忽略收到的信号

    signal(SIGQUIT, handler);
    //SIGQUIT代表 ctrl+\ 发起的信号,收到这个信号后转去执行handler函数。

    int i = 0;
    for(; ;)
    {
        printf("哈哈,死不了!\n");
        sleep(1);
    }
}

这里写图片描述


2. 给进程发送信号 —— kill()函数

1.使用指令发送:kill -[信号值] [pid]
 例如使用kill -SIGSEGV [pid]杀死进程:

[tian@localhost 10-信号]$ ps -ef | grep a.out
tian     10538  9053  0 00:32 pts/7    00:00:00 ./a.out
tian     10541 10307  0 00:33 pts/6    00:00:00 grep a.out
[tian@localhost 10-信号]$ kill -SIGSEGV 10538
[tian@localhost 10-信号]$ ps -ef | grep a.out
tian     10546 10307  0 00:33 pts/6    00:00:00 grep a.out

2.使用函数发送——kill()

#include <sys/types.h>
#include <signal.h>

int kill(int pid, int signum)

函数功能:用于向任何进程组或进程发送信号。

第一个参数pid
  pid > 0:将信号发送给进程id为pid的进程;
  pid = 0:将信号发送给调用者所在进程组中所有进程;
  pid = -1:将信号发送给所有进程,除了1号进程;
  pid < -1:将信号发送给进程组id为|pid|的进程组中所有进程

第二个参数signum
  准备发送的信号的信号值,即信号编号。假如其值为零则没有任何信号送出,但是系统会执行错误检查,通常会利用sig值为零来检验某个进程是否仍在执行。

返回值:
  成功执行时,返回0。失败返回-1

代码示例:

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

void handler(int n)
{
    printf("收到,over!\n");
}

int main()
{
    signal(SIGUSR1, handler); //收到SIGUSR1后,转去执行handler函数
    pid_t pid = fork();
    if(pid == 0){  //子进程
        kill(getppid(), SIGUSR1);//在子进程中向父进程发送一个SIGUSR1信号
        exit(0);

    }else{  //父进程
        while(1){
            printf("收到请回话!\n");
            fflush(stdout);
            sleep(1);
        }
    }
}

测试结果:

[tian@localhost 10-信号]$ ./a.out 
收到请回话!
收到,over!
收到请回话!
收到请回话!
收到请回话!
收到请回话!
^C
[tian@localhost 10-信号]$ 

3. 给本进程发信号——raise()函数

#include <signal.h>

int raise(int sig);

  返回值:成功返回0,失败返回非0;
    
  这个函数也可以用kill()函数实现: kill(getpid(), sig)


4. 给进程组发信号 —— killpg()函数

#include <signal.h>

int killpg(int pgrp, int sig);

  返回值:成功返回0,失败返回-1;


三、常用的SIGALRM信号

1. 闹钟信号 —— alarm()函数

#include <unistd.h>

unsigned int alarm(unsigned int seconds);
所需头文件 #include <unistd.h>
函数原型 unsigned int alarm(unsigned int seconds);
参数seconds 执行秒数,系统经过second秒后向该进程发送SIGALRM信号
函数返回值 成功:如调用此alarm()函数前,进程中已设置了闹钟,则返回上个闹钟的剩余时间,否则返回0。 失败:-1

  可以在进程中设置一个定时器,等到时间到达时,就会给进程发送SIGALARM信号,注意的是一个进程只能有一个闹钟时间,如果调用alarm()之前已经设置了闹钟时间,那么任何以前的闹钟时间都会被新值所代替。如果second设为0,表示清除闹钟。

代码示例:

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

void handler(int n)
{
    printf("\n超时,退出!\n");
    exit(1);
}

int main()
{
    char buff[100];
    printf("your name: ");

    signal(SIGALRM, handler);
    alarm(3); //设置闹钟,3s后发送信号
    scanf("%s", &buff);
    alarm(0); //清除闹钟

    printf("get  name: %s\n", buff);

    for(; ;)
    {
        fflush(stdout);
        printf("……");
        sleep(1);
    }
}

测试结果:

[tian@localhost 10-信号]$ ./a.out 
your name: 
超时,退出!
[tian@localhost 10-信号]$ ./a.out 
your name: 张三
get  name: 张三
…………………………………………^C
[tian@localhost 10-信号]$ 

附加1(重要信号的含义):

  1. SIGHUP :当用户退出Shell时,由该Shell启的发所有进程都退接收到这个信号,默认动作为终止进程。

  2. SIGINT :用户按下组合键(ctrl + c)时,用户端时向正在运行中的由该终端启动的程序发出此信号。默认动作为终止进程。

  3. SIGQUIT :当用户按下组合键(ctrl + \)时产生该信号,用户终端向正在运行中的由该终端启动的程序发出此信号。默认动作为终止进程并产生core文件。

  4. SIGILL :CPU检测到某进程执行了非法指令。默认动作为终止进程并产生core文件。

  5. SIGTRAP:该信号由断点指令或其他trap指令产生。默认动作为终止进程并产生core文件。

  6. SIGABRT :调用abort函数时产生该信号。默认动作为终止进程并产生core文件。

  7. SIGBUS:非法访问内存地址,包括内存地址对齐(alignment)出错,默认动作为终止进程并产生core文件。

  8. SIGFPE:在发生致命的算术错误时产生。不仅包括浮点运行错误,还包括溢出及除数为0等所有的算术错误。默认动作为终止进程并产生core文件。

  9. SIGKILL :无条件终止进程。本信号不能被忽略、处理和阻塞。默认动作为终止进程。它向系统管理员提供了一种可以杀死任何进程的方法。

  10. SIGUSR1:用户定义的信号,即程序可以在程序中定义并使用该信号。默认动作为终止进程。

  11. SIGSEGV:指示进程进行了无效的内存访问。默认动作为终止进程并使用该信号。默认动作为终止进程。

  12. SIGUSR2:这是另外一个用户定义信号,程序员可以在程序中定义并使用该信号。默认动作为终止进程。

  13. SIGPIPE :Broken pipe:向一个没有读端的管道写数据。默认动作为终止进程。

  14. SIGALRM :定时器超时,超时的时间由系统调用alarm设置。默认动作为终止进程。

  15. SIGTERM :程序结束(terminate)信号,与SIGKILL不同的是,该信号可以被阻塞和处理。通常用来要求程序正常退出。执行Shell命令kill时,缺少产生这个信号。默认动作为终止进程。

  16. SIGCHLD :子程序结束时,父进程会收到这个信号。默认动作为忽略该信号。

  17. SIGCONT:让一个暂停的进程继续执行。

  18. SIGSTOP:停止(stopped)进程的执行。注意它和SIGTERM以及SIGINT的区别:该进程还未结束,只是暂停执行。本信号不能被忽略、处理和阻塞。默认作为暂停进程。

  19. SIGTSTP:停止进程的动作,但该信号可以被处理和忽略。按下组合键时发出该信号。默认动作为暂停进程。

  20. SIGTTIN:当后台进程要从用户终端读数据时,该终端中的所有进程会收到SIGTTIN信号。默认动作为暂停进程。

  21. SIGTTOU:该信号类似于SIGTIN,在后台进程要向终端输出数据时产生。默认动作为暂停进程。

  22. SIGURG :套接字(socket)上有紧急数据时,向当前正在运行的进程发出此信号,报告有紧急数据到达。默认动作为忽略该信号。

  23. SIGXCPU:进程执行时间超过了分配给该进程的CPU时间,系统产生该信号并发送给该进程。默认动作为终止进程。

  24. SIGXFSZ:超过文件最大长度的限制。默认动作为yl终止进程并产生core文件。

  25. SIGVTALRM:虚拟时钟超时时产生该信号。类似于SIGALRM,但是它只计算该进程占有用的CPU时间。默认动作为终止进程。

  26. SIGPROF:类似于SIGVTALRM,它不仅包括该进程占用的CPU时间还抱括执行系统调用的时间。默认动作为终止进程。

  27. SIGWINCH:窗口大小改变时发出。默认动作为忽略该信号。

  28. SIGIO:此信号向进程指示发出一个异步IO事件。默认动作为忽略。

  29. SIGPWR:关机。默认动作为终止进程。

猜你喜欢

转载自blog.csdn.net/tianzez/article/details/80254692