Linux中信号是什么?Ctrl + c后到底为什么会中断程序?

简介: 信号在进程的学习中是一个非常好用的存在,它是软件层次上对中断机制的一种模拟,是异步通信方式,同时也可以用来检测用户空间到底发生了什么情况,然后系统知道后就可以做出相应的对策。

 

目录

信号的基本特征:

实际应用场景:

使用信号的注意事项:

信号的种类:

信号的三大类型:

源码:


信号在进程的学习中是一个非常好用的存在,它是软件层次上对中断机制的一种模拟,是异步通信方式,同时也可以用来检测用户空间到底发生了什么情况,然后系统知道后就可以做出相应的对策。

在Linux系统中,信号(Signal)是一种进程间通信(IPC)的方式,它用于通知进程发生了某个特定的事件。信号机制允许操作系统或一个进程向另一个进程发送异步通知,以此来控制进程的行为。信号是软中断,可以打断进程的正常流程,迫使其提前处理信号所代表的事件。以下是关于Linux信号的一些基本概念和要点:

信号的基本特征:

  1. 信号的命名:Linux中的信号通常以“SIG”前缀开始,例如SIGINT(中断,通常由Ctrl+C触发)、SIGTERM(终止进程)、SIGHUP(挂起)、SIGKILL(强制终止进程)等。每个信号都有一个对应的数字编号。
  2. 信号处理:进程可以有三种方式处理信号:
  • 默认处理:大多数信号有默认行为,比如SIGINT会使进程终止。
  • 忽略:进程可以选择忽略某些信号,但某些关键信号如SIGKILL和SIGSTOP不能被忽略。
  • 自定义处理:通过信号处理函数(signal handler)来指定信号到达时执行的特定操作。
  1. 信号发送:进程可以通过kill系统调用向自己或其他进程发送信号。内核也会在特定情况下自动发送信号,比如用户操作(如按下Ctrl+C)或硬件异常。
  2. 信号阻塞与未决:进程可以通过sigprocmask等函数临时阻塞对某些信号的接收,直到它解除阻塞。同时,未决信号是指已发送但尚未被进程处理的信号,这些信号会被排队等待处理。
  3. 信号与系统调用的关系:信号的处理通常与当前执行的系统调用无关,信号处理可以中断正常的程序执行流程,执行完信号处理函数后,可以选择恢复原系统调用(如果支持)或直接返回到用户态。

实际应用场景:

  • 用户中断:用户通过键盘(如Ctrl+C)发送SIGINT来中断一个正在运行的命令或进程。
  • 程序终止:系统管理员或程序本身使用SIGTERM来请求进程正常退出。
  • 程序重启:SIGHUP信号常用于通知进程重新加载配置文件或重启。
  • 调试:SIGTRAP等信号用于调试目的,可以捕获并分析程序状态。

使用信号的注意事项:

  • 信号处理函数应当简短且安全,避免在其中执行长时间或阻塞的操作。
  • 某些信号(如SIGKILL和SIGSTOP)不能被捕获或忽略,以确保系统能够控制进程。
  • 合理设计信号处理逻辑,避免信号处理时的竞态条件和死锁。

理解信号及其机制对于编写健壮的Linux应用程序至关重要,尤其是在需要处理外部事件或异常情况的场景下。

信号的种类:

SIGINT:结束进程,对应快捷方式ctrl+c

SIGQUIT:退出信号,对应快捷方式ctrl+\

SIGKILL:结束进程,不能被忽略不能被捕捉

SIGTERM:结束终端进程,kill 使用时不加数字默认是此信号

SIGCHLD:子进程状态改变时给父进程发的信号

SIGSTOP:暂停进程,不能被忽略不能被捕捉

SIGTSTP:暂停信号,对应快捷方式ctrl+z

SIGALRM:闹钟信号,alarm函数设置定时,当到设定的时间时,内核会向进程发送此信号结束进程。

信号的三大类型:

1)忽略信号:对信号不做任何处理,但是有两个信号不能忽略:即SIGKILL及SIGSTOP。

2)捕捉信号:定义信号处理函数,当信号发生时,执行相应的处理函数。

3)执行缺省操作:Linux对每种信号都规定了默认操作 。

这个程序是运用了信号的一个顺序的简单小程序,我们只需要把逻辑顺清,谁应该接收什么信号,做什么处理,忽略什么信号就可以写出,但是有一点要注意的是如何获取父进程和子进程的ID号。

getPPID:获取父进程ID号

getPID:获取子进程ID号

值得一提的是虽然子进程复制了父进程几乎所有的东西,但是他们的ID号是不同的

用信号的知识实现司机和售票员问题。

1)售票员捕捉SIGINT(代表开车)信号,向司机发送SIGUSR1信号,司机打印(let's gogogo)

2)售票员捕捉SIGQUIT(代表停车)信号,向司机发送SIGUSR2信号,司机打印(stop the bus)

3)司机捕捉SIGTSTP(代表到达终点站)信号,向售票员发送SIGUSR1信号,售票员打印(please get off the bus)

4)司机等待售票员下车,之后司机再下车。

源码:

#include <sys/types.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
pid_t pid;
void hanlder1(int sig)
{
    if(sig == SIGINT)
    {   
        kill(getppid(), SIGUSR1);// getppid() 是父进程的号
    }
    if(sig == SIGQUIT)
    {
        kill(getppid(), SIGUSR2);
    }
    if(sig == SIGUSR1)
    {
        printf("get off the bus\n");
        exit(0);
    }
    
}
void hanlder2(int sig)
{
    if(sig == SIGUSR1)
    {
        printf("let's gogogo\n");
    }
    if(sig == SIGUSR2)
    {
         printf("stop the bus\n");
    }
    if(sig == SIGTSTP)
    {
        kill(pid,SIGUSR1);// pid 是子进程的号
        wait(NULL);
        exit(0);
    }
}
int main(int argc, char const *argv[])
{
    pid = fork();
    if (pid < 0)
    {
        perror("pid open error");
        return -1;
    }
    else if (pid == 0)
    {
        while (1)
        {
            signal(SIGTSTP,SIG_IGN);// 忽略SIGTSTP
            signal(SIGINT, hanlder1);// 捕获SIGINT
            signal(SIGQUIT, hanlder1);//捕获SIGQUIT
            signal(SIGUSR1, hanlder1);//捕获SIGUSR1
            pause();
        }
    }
    else
    {
        while (1)
        {
            signal(SIGINT, SIG_IGN);// 忽略SIGINT
            signal(SIGQUIT, SIG_IGN);// 忽略SIGQUIT
            signal(SIGUSR1, hanlder2);//捕获SIGUSR1
            signal(SIGUSR2, hanlder2);//捕获SIGUSR2
            signal(SIGTSTP, hanlder2);//捕获STGTSTP
            pause();
        }
    }
}

image.gif


相关文章
|
3月前
|
安全 Linux Shell
Linux上执行内存中的脚本和程序
【9月更文挑战第3天】在 Linux 系统中,可以通过多种方式执行内存中的脚本和程序:一是使用 `eval` 命令直接执行内存中的脚本内容;二是利用管道将脚本内容传递给 `bash` 解释器执行;三是将编译好的程序复制到 `/dev/shm` 并执行。这些方法虽便捷,但也需谨慎操作以避免安全风险。
195 6
|
4月前
|
网络协议 Linux
Linux查看端口监听情况,以及Linux查看某个端口对应的进程号和程序
Linux查看端口监听情况,以及Linux查看某个端口对应的进程号和程序
682 2
|
4月前
|
Linux Python
linux上根据运行程序的进程号,查看程序所在的绝对路径。linux查看进程启动的时间
linux上根据运行程序的进程号,查看程序所在的绝对路径。linux查看进程启动的时间
72 2
|
2月前
|
运维 Java Linux
【运维基础知识】Linux服务器下手写启停Java程序脚本start.sh stop.sh及详细说明
### 启动Java程序脚本 `start.sh` 此脚本用于启动一个Java程序,设置JVM字符集为GBK,最大堆内存为3000M,并将程序的日志输出到`output.log`文件中,同时在后台运行。 ### 停止Java程序脚本 `stop.sh` 此脚本用于停止指定名称的服务(如`QuoteServer`),通过查找并终止该服务的Java进程,输出操作结果以确认是否成功。
45 1
|
3月前
|
消息中间件 分布式计算 Java
Linux环境下 java程序提交spark任务到Yarn报错
Linux环境下 java程序提交spark任务到Yarn报错
45 5
|
4月前
|
NoSQL Linux C语言
嵌入式GDB调试Linux C程序或交叉编译(开发板)
【8月更文挑战第24天】本文档介绍了如何在嵌入式环境下使用GDB调试Linux C程序及进行交叉编译。调试步骤包括:编译程序时加入`-g`选项以生成调试信息;启动GDB并加载程序;设置断点;运行程序至断点;单步执行代码;查看变量值;继续执行或退出GDB。对于交叉编译,需安装对应架构的交叉编译工具链,配置编译环境,使用工具链编译程序,并将程序传输到开发板进行调试。过程中可能遇到工具链不匹配等问题,需针对性解决。
105 3
|
4月前
|
Linux 调度
Linux0.11 信号(十二)(下)
Linux0.11 信号(十二)
27 1
|
4月前
|
Linux Windows Python
最新 Windows\Linux 后台运行程序注解
本文介绍了在Windows和Linux系统后台运行程序的方法,包括Linux系统中使用nohup命令和ps命令查看进程,以及Windows系统中通过编写bat文件和使用PowerShell启动隐藏窗口的程序,确保即使退出命令行界面程序也继续在后台运行。
|
4月前
|
存储 Unix Linux
Linux0.11 信号(十二)(上)
Linux0.11 信号(十二)
34 0
|
4月前
|
存储 安全 Linux
【Azure 应用服务】App Service For Linux 怎么安装Composer,怎么安装PHP扩展,怎么来修改站点根路径启动程序?
【Azure 应用服务】App Service For Linux 怎么安装Composer,怎么安装PHP扩展,怎么来修改站点根路径启动程序?