欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Linux —— 进程信号

程序员文章站 2022-07-12 11:18:25
...

一、信号的基本概念

1. 什么是信号

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


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

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

3. 如何查看信号

  1. 查看系统中的信号: kill -l;
  2. 查看信号的默认处理: man 7 signal;
  3. 有的信号有多个值,对应不同系统(中间那个值对应Linux系统)
    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);
    }
}

Linux —— 进程信号


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:关机。默认动作为终止进程。