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

 

目录

信号的基本特征:

实际应用场景:

使用信号的注意事项:

信号的种类:

信号的三大类型:

源码:


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

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

信号的基本特征:

  1. 信号的命名:Linux中的信号通常以“SIG”前缀开始,例如SIGINT(中断,通常由Ctrl+C触发)、SIGTERM(终止进程)、SIGHUP(挂起)、SIGKILL(强制终止进程)等。每个信号都有一个对应的数字编号。

  2. 信号处理:进程可以有三种方式处理信号:

    • 默认处理:大多数信号有默认行为,比如SIGINT会使进程终止。
    • 忽略:进程可以选择忽略某些信号,但某些关键信号如SIGKILL和SIGSTOP不能被忽略。
    • 自定义处理:通过信号处理函数(signal handler)来指定信号到达时执行的特定操作。
  3. 信号发送:进程可以通过kill系统调用向自己或其他进程发送信号。内核也会在特定情况下自动发送信号,比如用户操作(如按下Ctrl+C)或硬件异常。

  4. 信号阻塞与未决:进程可以通过sigprocmask等函数临时阻塞对某些信号的接收,直到它解除阻塞。同时,未决信号是指已发送但尚未被进程处理的信号,这些信号会被排队等待处理。

  5. 信号与系统调用的关系:信号的处理通常与当前执行的系统调用无关,信号处理可以中断正常的程序执行流程,执行完信号处理函数后,可以选择恢复原系统调用(如果支持)或直接返回到用户态。

实际应用场景:

  • 用户中断:用户通过键盘(如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();
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

笨笨小乌龟11

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值