您现在的位置是:首页 >技术教程 >信号发送与处理网站首页技术教程

信号发送与处理

repinkply 2024-08-09 12:01:02
简介信号发送与处理

问题

按下 CTRL + C之后,命令行中的前台进程会被终止。为什么???

什么是信号?

信号是一种“软件中断”,用来处理异步事件

  1. 内核发送信号到某个进程,通知进程事件的发生
  2. 事件可能来自硬件,可能来自用户输入,可能来自除零错误

信号是一种类型的进程间通信方式(一个进程向另外一个进程发送信号)

  1. A进程发送事件T,向B进程发送信号,B进程执行动作响应事件
  2. 进程可以对接收的不同信号进行不同动作响应(信号 -> 处理)

信号的分类

硬件异常

        内核检测到硬件错误,发送相应信号给相关进程

终端信号(用户交互信号,最典型:ctrl+c)

        在终端输入 “特殊字符” 等价于向前台进程组发送信号

软件信号

        在软件层面(进程代码中)触发的信号(发送给自身或其他进程)

硬件异常信号

信号说明
SIGBUS7总线错误,进程发生了内存访问错误
SIGFPE8算术错误,FPE表示浮点异常
SIGILL9指令错误,进程尝试执行非法指令
SIGSEGV11段错误,进程访问了非法内存区域

终端相关信号

  1. SIGINT(Ctrl + c) :程序终止信号,用于通知前台进程组终止进程
  2. SIGQUIT(Ctrl + ) :与SIGINT类似,进程收到该信号退出时可产生 coredump 文件
  3. SIGTSTP(Ctrl + Z) :停止进程的运行,进程收到该信号后可以选择处理或忽略。Ctrl + Z :进程收到该信号后停止运行(状态发生转换),后续可以恢复运行状态。

软件相关信号

  1. 子进程退出:父进程收到 SIGCHLD 信号
  2. 父进程退出:子进程可能收到信号(什么信号?)
  3. 定时器到期:alarm(),ualarm(),timer_create(),...
  4. 主动发信号:kill(),raise(),...

内核与信号

1.内核检测到相关的错误,将信号发送给对应的进程。

2.进程可以通过系统调用,告诉内核,自己需要信号。比如:时钟信号。比如进程A告诉内核,我需要时钟信号,那么内核就定期给进程A发送时钟信号。

3.进程间通信:进程A通过内核 将信号 发送给进程B

信号的发送与处理,无非就是以上三种情况。

信号的默认处理

默认处理方式说明示例
ignore进程丢弃信号不会产生任何影响SIGCHLD SIGURG
terminate终止进程SIGKILL SIGHUP
coredump终止进程并产生转储文件SIGQUIT SIGILL
stop/continue停止进程执行/恢复进程执行SIGSTOP,SIGCONT

知识加油站:System V vs BSD

System V :也被称为 AT&T SystemV,是Unix操作系统众多版本中的一支

BSD :加州大学巴克利分校开创,Unix衍生系统,代表由此派生出的各种套件集合

Linux之所以被称为类Unix操作系统(Unix Like),部分原因就是Linux的操作风格是介于上述二者之间,且不同的厂商为了照顾不同的用户,其发行版本的操作风格有存在差异。

自定义信号发送

#include <sys/types.h>

#include <signal.h>

int kill(pid_t pid,int sig);

//pid > 0 ==> 发送信号给进程 ,pid == 0 ==> 发送信号给进程组 ,pid < -1  比如-1000,将信号发送给PGID为-1000的进程组

int raise(int sig); //信号处理完毕才返回

notes:

标准信号是unix系统中的信号,编号范围从1到31。

实时信号是Linux独有的信号,编号范围从32到64。

下面来看几个demo示例。

signal

 可以看到在终端按下 CTRL + C 之后,信号处理函数 signal_handler就不断地调用。

sysv_signal

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>

void signal_handler(int sig)
{
    printf("handler : sig = %d
",sig);
}

int main(void)
{
    //signal(SIGINT,signal_handler);
    sysv_signal(SIGINT,signal_handler);

    while(1)
    {
        sleep(1);
    }

    return 0;
}
zhaixue@ubuntu:~/DTLinux/0-11$ gcc main.c -o main.out
zhaixue@ubuntu:~/DTLinux/0-11$ ./main.out 
^Chandler : sig = 2
^C
zhaixue@ubuntu:~/DTLinux/0-11$ 

bsd_signal

//#define _GNU_SOURCE
#define  _XOPEN_SOURCE  600
#define _POSIX_C_SOURCE 200800L

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>

void signal_handler(int sig)
{
    printf("handler : sig = %d
",sig);
}

int main(void)
{
    //signal(SIGINT,signal_handler);
    //sysv_signal(SIGINT,signal_handler);
    bsd_signal(SIGINT,signal_handler);

    while(1)
    {
        sleep(1);
    }

    return 0;
}
zhaixue@ubuntu:~/DTLinux/0-11$ gcc main.c -o main.out
zhaixue@ubuntu:~/DTLinux/0-11$ ./main.out 
^Chandler : sig = 2
^Chandler : sig = 2
^Chandler : sig = 2
^Chandler : sig = 2
^Chandler : sig = 2
^Chandler : sig = 2
^Chandler : sig = 2
^Quit (core dumped)
zhaixue@ubuntu:~/DTLinux/0-11$ 

发送自定义信号

//未完待续...

思考

以上signal、sysv_signal、bsd_signal 三种自定义信号处理函数有什么区别?

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。