0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

linux内核信号是如何处理的?看完全懂了……

电子设计 来源:互联网 作者:佚名 2017-11-16 05:11 次阅读

本文简单介绍下Linux信号处理机制,为介绍二进制翻译下信号处理机制做一个铺垫。
本文主要参考书目《Linux内核源代码情景分析》《独辟蹊径品内核:Linux内核源代码导读》
首先,先说一下什么是信号。信号本质上是在软件层次上对中断机制的一种模拟,其主要有以下几种来源:

程序错误:除零,非法内存访问…

外部信号:终端Ctrl-C产生SGINT信号,定时器到期产生SIGALRM…

显式请求:kill函数允许进程发送任何信号给其他进程或进程组。

在Linux下,可以通过以下命令查看系统所有的信号:

kill-l

可以通过类似下面的命令显式的给一个进程发送一个信号:

kill-2pid

上面的命令将2号信号发送给进程id为pid的进程。不存在编号为0的信号。

目前Linux支持64种信号。信号分为非实时信号(不可靠信号)和实时信号(可靠信号)两种类型,对应于 Linux 的信号值为 1-31 和 34-64。信号是异步的,一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知道信号到底什么时候到达。本文着重于Linux的信号处理机制,对信号更多的介绍可以参考这里。

一般情况下一个进程接受到信号后,会有如下的行为:

进程对信号的响应

忽略信号:大部分信号可被忽略,除SIGSTOP和SIGKILL信号外(这是超级用户杀掉或停掉任意进程的手段)。

捕获信号:注册信号处理函数,它对产生的特定信号做处理。

让信号默认动作起作用:unix内核定义的默认动作,有5种情况:

a) 流产abort:终止进程并产生core文件。

b) 终止stop:终止进程但不生成core文件。

c) 忽略:忽略信号。

d) 挂起suspend:挂起进程。

e) 继续continue:若进程是挂起的,则resume进程,否则忽略此信号。

注册信号处理函数

如果想要进程捕获某个信号,然后作出相应的处理,就需要注册信号处理函数。同中断类似,内核也为每个进程准备了一个信号向量表,信号向量表中记录着每个信号所对应的处理机制,默认情况下是调用默认处理机制。当进程为某个信号注册了信号处理程序后,发生该信号时,内核就会调用注册的函数。

注册信号处理函数是通过系统调用signal()、sigaction()。其中signal()在可靠信号系统调用的基础上实现, 是库函数。它只有两个参数,不支持信号传递信息,主要是用于前32种非实时信号的安装;而sigaction()是较新的函数(由两个系统调用实 现:sys_signal以及sys_rt_sigaction),有三个参数,支持信号传递信息,主要用来与 sigqueue() 系统调用配合使用,当然,sigaction()同样支持非实时信号的安装。sigaction()优于signal()主要体现在支持信号带有参数。关于这方面的内容,如果想获取更多,也可参考这里。

Linux下信号处理机制

进程如何发现和接受信号?

我们知道,信号是异步的,一个进程不可能等待信号的到来,也不知道信号会到来,那么,进程是如何发现和接受信号呢?实际上,信号的接收不是由用户进程来完成的,而是由内核代理。当一个进程P2向另一个进程P1发送信号后,内核接受到信号,并将其放在P1的信号队列当中。当P1再次陷入内核态时,会检查信号队列,并根据相应的信号调取相应的信号处理函数。如下图所示:


其中,动作c:发现和捕捉信号

信号检测和响应时机

刚才我们说,当P1再次陷入内核时,会检查信号队列。那么,P1什么时候会再次陷入内核呢?陷入内核后在什么时机会检测信号队列呢?

当前进程由于系统调用、中断或异常而进入系统空间以后,从系统空间返回到用户空间的前夕。

当前进程在内核中进入睡眠以后刚被唤醒的时候(必定是在系统调用中),或者由于不可忽略信号的存在而提前返回到用户空间。

进入信号处理函数

发现信号后,根据信号向量,知道了处理函数,那么该如何进入信号处理程序,又该如何返回呢?

我们知道,用户进程提供的信号处理函数是在用户态里的,而我们发现信号,找到信号处理函数的时刻处于内核态中,所以我们需要从内核态跑到用户态去执行信号处理程序,执行完毕后还要返回内核态。这个过程如下图所示:

如图中所见,处理信号的整个过程是这样的:进程由于 系统调用或者中断 进入内核,完成相应任务返回用户空间的前夕,检查信号队列,如果有信号,则根据信号向量表找到信号处理函数,设置好“frame”后,跳到用户态执行信号处理函数。信号处理函数执行完毕后,返回内核态,设置“frame”,再返回到用户态继续执行程序。

在上面这段话中,我提到“frame”,frame是什么?那么为什么要设置frame?为什么在执行完信号处理函数后还要返回内核态呢?

什么叫Frame?

在调用一个子程序时,堆栈要往下(逻辑意义上是往上)伸展,这是因为需要在堆栈中保存子程序的返回地址,还因为子程序往往有局部变量,也要占用堆栈中的空间。此外,调用子程序时的参数也是在堆栈中。子程序调用嵌套越深,则堆栈伸展的层次也越多。在堆栈中的每一个这样的层次,就称为一个”框架”,即frame。

一般来说,当子程序和调用它的程序在同一空间中时,堆栈的伸展,也就是堆栈中框架的建立,过程主要如下:

call指令将返回地址压入堆栈(自动)

用push指令压入调用参数

调整堆栈指针来分配局部变量

为什么以及怎么设置frame?

我们知道,当进程陷入内核态的时候,会在堆栈中保存中断现场。因为用户态和内核态是两个运行级别,所以要使用两个不同的栈。当用户进程通过系统调用刚进入内核的时候,CPU会自动在该进程的内核栈上压入下图所示的内容:(图来自《Linux内核完全注释》)

在处理完系统调用以后,就要调用do_signal()函数进行设置frame等工作。这时内核堆栈的状态应该跟下图左半部分类似(系统调用将一些信息压入栈了):

在找到了信号处理函数之后,do_signal函数首先把内核堆栈中存放返回执行点的eip保存为old_eip,然后将eip替换为信号处理函数的地址,然后将内核中保存的“原ESP”(即用户态栈地址)减去一定的值,目的是扩大用户态的栈,然后将内核栈上的内容保存到用户栈上,这个过程就是设置frame.值得注意的是下面两点:

之所以把EIP的值设置成信号处理函数的地址,是因为一旦进程返回用户态,就要去执行信号处理程序,所以EIP要指向信号处理程序而不是原来应该执行的地址。

之所以要把frame从内核栈拷贝到用户栈,是因为进程从内核态返回用户态会清理这次调用所用到的内核栈(类似函数调用),内核栈又太小,不能单纯的在栈上保存另一个frame(想象一下嵌套信号处理),而我们需要EAX(系统调用返回值)、EIP这些信息以便执行完信号处理函数后能继续执行程序,所以把它们拷贝到用户态栈以保存起来。

以上这些搞清楚之后,下面的事情就顺利多了。这时进程返回用户空间,就会根据内核栈中的EIP值执行信号处理函数。那么,信号处理程序执行完后,怎么返回程序继续执行呢?

信号处理函数执行完后怎么办?

信号处理程序执行完毕之后,进程会主动调用sigreturn()系统调用再次回到内核,查看有没有其他信号需要处理,如果没有,这时内核就会做一些善后工作,将之前保存的frame恢复到内核栈,恢复eip的值为old_eip,然后返回用户空间,程序就能够继续执行。至此,内核遍完成了一次(或几次)信号处理工作。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • Linux
    +关注

    关注

    87

    文章

    10970

    浏览量

    206666
  • 数字信号处理

    关注

    15

    文章

    513

    浏览量

    45277
  • IP内核
    +关注

    关注

    0

    文章

    13

    浏览量

    12766
收藏 人收藏

    评论

    相关推荐

    减速机渗油问题如何处理

    电子发烧友网站提供《减速机渗油问题如何处理.docx》资料免费下载
    发表于 03-05 09:18 0次下载

    Linux内核信号的传递过程

    前面我们已经介绍了内核注意到信号的到来,调用相关函数更新进程描述符以便进程接收处理信号。但是,如果目标进程此时没有运行,内核则推迟传递
    的头像 发表于 01-17 09:51 453次阅读
    <b class='flag-5'>Linux</b><b class='flag-5'>内核</b>中<b class='flag-5'>信号</b>的传递过程

    一文详解Linux内核-信号的产生过程

    许多内核函数产生信号:它们完成信号处理的第一阶段,也就是更新一个或多个进程描述符。
    的头像 发表于 01-13 13:48 655次阅读

    Linux内核信号详解

    5.18.18的定义 4.2.4 ARM/linux5.18.18的定义 4.2.5 RISC-V/linux6.7 4.1 信号描述符和信号处理
    的头像 发表于 01-13 09:40 747次阅读
    <b class='flag-5'>Linux</b><b class='flag-5'>内核</b>中<b class='flag-5'>信号</b>详解

    示波器如何处理有噪声的信号

    示波器如何处理有噪声的信号  示波器是一种用于测量和显示电信号的设备,可以帮助工程师和科学家分析和诊断电路故障。然而,在实际的测量中,信号往往会受到各种噪声的干扰,这可能会导致测量结果
    的头像 发表于 12-21 15:37 543次阅读

    获取Linux内核源码的方法

    (ELF1/ELF1S开发板及显示屏)Linux内核是操作系统中最核心的部分,它负责管理计算机硬件资源,并提供对应用程序和其他系统组件的访问接口,控制着计算机的内存、处理器、设备驱动程序和文
    的头像 发表于 12-13 09:49 282次阅读
    获取<b class='flag-5'>Linux</b><b class='flag-5'>内核</b>源码的方法

    ADAS1000发送寄存器后会偶尔出现心电信号完全消失的情况,要如何处理

    您好, 目前我使用ADAS1000做心电模拟前端,已经获取到心电信号和呼吸信号,但是遇到一个问题,在运行过程中,我想发送命令来更改寄存器配置,来更改ADAS的工作模式,发现发送寄存器后会偶尔出现心电信号
    发表于 12-13 08:26

    AD8302输入信号何处理

    在使用AD8302的时候,芯片手册上描述输入信号为-60dBm~0dBm在50Ω的系统中,转换过来就是223.61uV~632.46mV的电压范围。 请问下,1. 如何控制输入信号在AD8302的要求范围内呢? 2.当信号高于这
    发表于 12-08 06:06

    何处理MOS管小电流发热?

    何处理MOS管小电流发热?
    的头像 发表于 12-07 15:13 275次阅读
    如<b class='flag-5'>何处理</b>MOS管小电流发热?

    什么是串扰?该如何处理它?

    什么是串扰?该如何处理它?
    的头像 发表于 12-05 16:39 415次阅读
    什么是串扰?该如<b class='flag-5'>何处理</b>它?

    Linux内核UDP收包为什么效率低

    现在很多人都在诟病Linux内核协议栈收包效率低,不管他们是真的懂还是一点都不懂只是听别人说的,反正就是在一味地怼Linux内核协议栈,他们的武器貌似只有DPDK。 但是,即便
    的头像 发表于 11-13 10:38 236次阅读
    <b class='flag-5'>Linux</b><b class='flag-5'>内核</b>UDP收包为什么效率低

    请问HC-SR04的回响信号是如何处理

    HC-SR04模块中,发送8个40KHZ的方波后,接受回波信号处理,然后在ECHO中输出一段与时间成正比的高电平。 我想问下,回波信号何处理?目前在做一个手机支架,为了省成本,不使
    发表于 10-26 08:26

    调试TrustZone时,如何处理HardFault?

    调试TrustZone时,如何处理HardFault?
    的头像 发表于 09-27 16:33 366次阅读
    调试TrustZone时,如<b class='flag-5'>何处理</b>HardFault?

    Linux内核如何使用结构体和函数指针?

    我将结合具体的Linux内核驱动框架代码来展示Linux内核如何使用结构体和函数指针。
    的头像 发表于 09-06 14:17 553次阅读
    <b class='flag-5'>Linux</b><b class='flag-5'>内核</b>如何使用结构体和函数指针?

    Linux内核的作用

    Linux操作系统是当今世界上最为广泛使用的开源操作系统之一,内核则是一个操作系统的核心和灵魂所在。对于一名Linux驱动开发者来说,了解Linux
    发表于 07-06 11:46 1189次阅读
    <b class='flag-5'>Linux</b><b class='flag-5'>内核</b>的作用