热门标签 | HotTags
当前位置:  开发笔记 > 前端 > 正文

Linux内核笔记--信号

Linux内核笔记--信号--Linux通用技术-Linux编程与内核信息,下面是详情阅读。
1 前言
写作本文的目的和其它文章略有不同,不是为了系统和全面的介绍”信号”这个子系统,--虽然它不复杂,其内容也不是一篇短短的文章所能够覆盖的,而是要回答自己的疑惑,解决工作中遇到的一些问题,理解那些不能够马上了解的部分。说到底,可以将这篇文章看作问题的答案。
曾经遇到的问题放在最后一节”问题与答案”中,在阅读正文之前先扫描一下问题可能更加有助于理解文章中的内容。
欢迎大家对这篇文章提出意见和指正,我的email是:shisheng_liu@yahoo.com.cn。

2 许可协议
本文的许可协议遵循GNU Free Document License。协议的具体内容请参见http://www.gnu.org/copyleft/fdl.html。在遵循GNU Free Document License的基础上,可以自由地传播或发行本文,但请保留本文的完整性。

3 什么是信号
信号是UNIX进程间通信的一种标准方式,在最早期的UNIX系统中已经存在。信号的出现允许内核和其它进程通知进程特定事件的发生。现代UNIX中也存在其它的进程间通信方式,但由于信号相对简单和有效,它们仍然被广泛使用。
信号是最简单的消息,当一个信号被发送时,它没有参数等附加信息,唯有一个整数来表示信号的值。信号的值在所有的UNIX系统中已经标准化了,每一个信号都有一个名字,它以三个字母SIG开头,对应于特定的事件。例如:SIGKILL表示终止进程;SIGBUS代表硬件故障;SIGCHLD代表子进程状态改变。
UNIX中常用的信号有31个,除了前面提到的三个信号外,还有如下信号是文章中用到的,其它的不再一一列出。
SIGSTOP 暂停程序执行
SIGCONT 恢复程序执行
3.1 进程对信号的处理
进程可以对每一个信号设置单独的处理方式,它可以忽略该信号,也可以设置自己的信号处理程序(称为捕捉),或者对信号什么也不做,信号发生的时候执行系统默认动作。
进程还可以设置对某些信号的阻塞(block)标志。如果一个信号被设置为阻塞,当信号发生的时候,它会与正常的信号一样被递送(deliver)给进程,但只有进程解除对信号的阻塞时才会被处理。
从一个非阻塞信号被递送给进程到信号得到处理之间的时间间隔,称为信号未决(pending)。有的资料将pending翻译为”信号挂起”。
所有的信号中,有两个信号SIGSTOP和SIGKILL是特殊的,他们既不能被捕捉,也不能被忽略,也不能被阻塞,这个特性保证了系统管理员在任何时候都可以用信号暂停和结束程序。
3.2 进程数据结构中与信号相关的部分
从这一部份开始,我们转入对linux内核部分的分析。在内核中,一个基本的数据结构sigset_t用来表示信号。不同的CPU架构中sigset_t的长度略有不同,对Intel i386架构来说,sigset_t是一个64位整数,每一位表示一个信号。Sigset_t中的后32位表示实时信号,它和普通信号的唯一区别是支持同一个信号的排队,这保证了发送的多个实时信号都被接收。实时信号是POSIX标准的一部分,但linux并没有对它做单独的处理,除了支持排队以外,因此它不是我们关心的重点。
进程结构task_struct中包含下列数据成员与信号相关
l sigpending
类型为int的一个标志,如果非0表示进程中存在着未决的非阻塞信号等待处理。
l pending
类型为struct sigpending的变量。可以看作进程的信号队列,它存放所有未决的信号,对某些信号来说,还包括相关的信息。例如SIGCHLD信号是子进程结束时发送给父进程的,它的附加信息中包括了子进程的ID和结束值。
l blocked
类型为sigset_t的变量。表示当前进程中哪些信号被阻塞。
l sig
类型为struct signal_struct的变量。描述进程怎样处理每个信号。
3.3 相关函数
下面是进程处理信号时经常用到的系统调用
l sigaction
设置或读取进程对某个信号的处理方式。进程可以忽略或用默认方式处理该信号,也可以设置自己的信号处理程序。
l Sigprocmask
设置或读取进程的信号阻塞掩码。
l Sigpending
返回当前未决的信号集。被阻塞的未决信号并不返回。
4 信号的发送
信号是异步事件,当一个信号被发送给进程时,接收进程可能运行在任何时刻,处于任何一种状态。为了使程序处于不同状态时都能够正确的处理信号,系统在信号发送的时候需要进行适当的预处理。我们讨论的过程实际上包括信号的产生和信号的递送,下面会进行详细的分析。
4.1 进程的状态
从进程本身的状态来看,它有可能处在运行态(RUNNING),等待态(INTERRUPTBLE &UNINTERRUPTBLE), 停止态(STOPPED)和僵死态(ZOMBIE)。而从进程运行的模式(mode)来看,又可能位于内核模式和用户模式的任一种,
4.2 程序分析
有几种方法可以将信号发送给某个进程,但它们最终会调用内核中的一个函数send_sig_info来完成发送。在这个函数里,内核会进行一系列检查,只有满足适当条件*的信号才会被放在进程的信号队列中;接着检查程序是否处于INTERRUPTABLE态,如果是就唤醒该进程,将进程的状态改为RUNNING态,并且放在系统运行队列内。
系统对发送信号的处理有两点比较有意思:
1) 只有SIGKILL和SIGCONT信号能够被状态为STOPPED进程接收。
所有其他信号都被忽略。这是由STOPPED状态本身的特性决定的,它被设置来控制进程的执行和暂停,SIGCONT信号能够使暂停的程序恢复执行,而SIGKILL被接收则提供杀死暂停进程的能力。
2)所谓满足适当的信号是满足一系列检查条件的信号:
.a)发送信号的进程满足POSIX.1中对发送者的要求
.b)该进程没有显式/隐含忽略该信号。
.c)该进程没有阻塞该信号。
.d)同样的信号没有已经在进程中挂起。同一进程的同一信号是不排队的,如果一个信号被连续发送多次,而它已经在接收进程中被挂起时,后续的信号被简单丢弃。
4.3 相关函数
4.3.1 内核部分
l send_sig_info(kernel/signal.c)
完成信号发送的入口函数,其他所有函数最终都通过它完成信号的发送。
l force_sig_info(kernel/signal.c)
仅供内核函数使用的强制信号发送函数。它做了一些努力以确保进程既不能忽略,又不能阻塞该信号,然后调用send_sig_info来完成信号的发送。
4.3.2 用户部分
l kill系统调用
进程通过kill系统调用向其他进程发送信号,kill系统调用在内核中的实现参见kernel/signal.c中的sys_kill() 函数,它调用send_sig_info来完成信号的发送。
Kill能够向一个进程或整个进程组发送信号。通过在kill系统调用时指定负的接收进程ID(”pid”),信号被发送给ID为”-pid”的进程组;如果将接收进程ID指定为-1,则信号被发送给除自己外的全体进程,这显然是一个不应该经常使用的功能。
5 信号的处理
5.1 信号处理程序
信号处理程序是进程本身的执行程序的一部分,只有进程正在CPU上运行的时候才能得到执行,也就是说,如果进程得不到执行的机会,例如状态是UNINTERRUPTABLE, STOPPED(如前面所说,可以接收SIGKILL, SIGCONT信号)等状态,发给进程的信号永远不会得到处理的机会。
5.2 何时执行
内核会在一些特定的时间点,-- 具体的说,在每一个系统调用结束,程序从内核态返回到用户态之前和程序从中断和异常代码中返回的时候[参见arch/kernel/entry.S文件中的ret_from_sys_call]代码 --, 检查当前进程是否有信号未被处理,然后调用信号处理的主函数do_signal。
5.3 程序分析
作为一个主函数,do_signal有相当多值得注意的地方。它循环地工作,每一个循环都从进程中取出一个未决的信号,取信号的顺序是由小到大,然后加以处理。处理的过程如下:
1) 如果程序正在被跟踪,程序会被转入STOPPED态,同时一个SIGCHLD信号被发送给跟踪者。一个有意思的事情是:当程序再次得到运行权后,它是怎样运行的呢?
2) 如果信号的处理操作是忽略该信号,则立刻完成本次循环,但SIGCHLD信号是一个例外,它调用sys_wait4函数,强迫这个进程读子进程的信息,借此清理由终止的子进程所留下的内存。
3) 如果信号的处理操作是默认处理,do_signal会执行信号的缺省操作,一个例外当接收进程是init时,信号被丢弃,本次循环会立即完成。
不同信号的缺省操作是不同的,可以分为四类,第一类信号的缺省操作是忽略,例如SIGCHLD信号;第二类信号会将进程转入停止态(注意,并不是结束进程),例如SIGSTOP信号;第三类信号会结束进程并将结束前的状态写入core文件,例如表示非法内存访问的SIGSEGV信号;而另一些信号仅仅简单的结束进程(do_exit函数),例如SIGKILL信号。
4)最后,调用进程本身的信号处理程序来处理信号,在完成了这个调用后,do_signal直接返回,也就是说,与其他被通过循环处理的信号不同,带有自己的信号处理函数的信号在一次do_signal调用中只能处理一个。这样做的原因是,与其它信号处理方式不同,进程的信号处理函数为用户态函数,它不可能直接在do_signal循环中被调用,否则会带来严重的安全性问题,do_signal能做的是设置程序的执行寄存器环境和堆栈代码,使进程回到用户态首先执行信号处理函数。设置进程的栈环境是一项相当复杂的工作,值得用另一篇文章来单独说明了,在此不再赘述,可以参考具体代码arch/kernel/signal.c中的handle_signal函数。

5.4 相关函数
所有的相关函数都位于内核态。
l ret_from_syscall
系统调用结束,返回到用户态前执行的函数。它检查进程中是否存在信号未决,并调用do_signal函数来处理未决的信号。Ret_from_syscall位于汇编文件arch/kernel/entry.S中,它其实并不是一个函数,而是一个汇编语言程序点,调用者用jmp指令来进入它。
l do_signal
信号处理的主函数。位于arch/kernel/signal.c文件中。
l handle_signal
处理有”自定义信号处理函数”的信号,完成建立堆栈等复杂工作。
6 信号与系统调用
6.1 允许被信号中断的系统调用
正常执行的系统调用是不会被信号中断的,但某些系统调用可能需要很长的等待时间来完成,对于这种情况,允许信号中断它的执行提供了更好的程序控制能力。要达到允许被信号中断的功能,系统调用的实现上需要满足如下条件:
系统调用必须在需要等待的时候将进程转入睡眠状态,主动让出CPU。因为作为内核态的程序,系统调用的执行是不可抢占的,不主动放弃CPU的系统调用会一直运行直到结束。而且当前进程的睡眠状态必须设置为TASK_INTERRUPTABLE,只有在这个状态下,当信号被发送给进程时,进程的状态被(信号发送函数)唤醒,并重新在运行队列中排队等待调度。
当进程获得了一个CPU时间片后,它接着睡眠时的下一条指令开始运行(还在系统调用内),系统调用判断出收到了信号,会设置一个与信号有关的退出标志并迅速结束。退出标志为:ERESTARTNOHAND,ERESTARTSYS,ERESTARTNOINTR中的一种,这些标志都是和接收信号程序do_signal通信用的,它们和进程对特定信号的处理标志一起,决定了系统调用中断后是否重新执行。
ERESTARTNOINTR:要求系统调用被无条件重新执行。
ERESTARTSYS: 要求系统调用重新执行,但允许信号处理程序根据自己的标志来做选择。
ERESTARTNOHAND:在信号没有设置自己的信号处理函数时系统调用重新执行。

6.2 系统调用的重新执行
在系统调用结束的时候,do_signal函数得以执行,它会与系统调用的退出标志一起来决定是否重新执行系统调用。do_sigal函数需要对两种情况进行处理,一种是进程对收到的信号设置了自己的信号处理程序,而另一种是进程没有设置收到的信号的信号处理程序,而且在处理完所有信号后,进程没有被停止或者结束掉。在后一种情况中,当系统调用退出的标志为ERESTARTNOHAND,ERESTARTSYS,ERESTARTNOINTR中的任意一个时,do_signal会使系统调用重新启动。而对前一种情况,只有系统调用的退出标志为ERESTARTNOINTR或者ERESTARTSYS并且信号处理标志也满足条件的时候系统调用才能重新启动。
6.3 相关函数
l sys_sigaction
系统调用sigaction的实现。通过sigaction系统调用,进程可以设置特定信号的处理程序和处理标志,其中一个标志SA_RESTART影响是否允许系统调用的重新执行。
7 问题与答案
l 为什么有时候进程阻塞于系统调用中时,该阻塞可以被信号中断;而阻塞于另一些系统调用的时候就不可以被中断?
如”信号与系统调用”一节所述,只有将进程以TASK_INTERRUPTABLE方式阻塞的系统调用才能够被中断。而设置以TASK_UNINTERRUPTABLE方式阻塞的系统调用不能中断。
l 系统是如何确保SIGKILL/SIGSTOP信号不能被捕捉和忽略的?
进程通过系统调用sigaction来设置对信号的处理方式,sigaction在内核中的实现函数是sys_sigaction(kernel/signal.c),它对进程传递信号值进行检查,确保信号SIGKILL和SIGSTOP不能被设置。
另外两个系统调用sigprocmask和sigsuspend会更改信号的屏蔽字,与sigaction类似,它们在内核中的实现函数会检查参数,确保SIGKILL和SIGSTOP不被屏蔽。
l 阻塞信号和忽略信号两种操作有何不同?
阻塞信号允许信号被发送给进程,但不进行处理,需要等到阻塞解除后再处理。而忽略信号是进程根本不接收该信号,所有被忽略的信号都被丢弃。
系统调用sigprocmask和sigsuspend被用来设置信号的阻塞与否;而系统调用sigaction则设置进程是否忽略一个信号。
推荐阅读
  • Nginx使用AWStats日志分析的步骤及注意事项
    本文介绍了在Centos7操作系统上使用Nginx和AWStats进行日志分析的步骤和注意事项。通过AWStats可以统计网站的访问量、IP地址、操作系统、浏览器等信息,并提供精确到每月、每日、每小时的数据。在部署AWStats之前需要确认服务器上已经安装了Perl环境,并进行DNS解析。 ... [详细]
  • 本文介绍了使用CentOS7.0 U盘刻录工具进行安装的详细步骤,包括使用USBWriter工具刻录ISO文件到USB驱动器、格式化USB磁盘、设置启动顺序等。通过本文的指导,用户可以轻松地使用U盘安装CentOS7.0操作系统。 ... [详细]
  • 这是原文链接:sendingformdata许多情况下,我们使用表单发送数据到服务器。服务器处理数据并返回响应给用户。这看起来很简单,但是 ... [详细]
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • Centos7.6安装Gitlab教程及注意事项
    本文介绍了在Centos7.6系统下安装Gitlab的详细教程,并提供了一些注意事项。教程包括查看系统版本、安装必要的软件包、配置防火墙等步骤。同时,还强调了使用阿里云服务器时的特殊配置需求,以及建议至少4GB的可用RAM来运行GitLab。 ... [详细]
  • 禁止程序接收鼠标事件的工具_VNC Viewer for Mac(远程桌面工具)免费版
    VNCViewerforMac是一款运行在Mac平台上的远程桌面工具,vncviewermac版可以帮助您使用Mac的键盘和鼠标来控制远程计算机,操作简 ... [详细]
  • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
  • 图解redis的持久化存储机制RDB和AOF的原理和优缺点
    本文通过图解的方式介绍了redis的持久化存储机制RDB和AOF的原理和优缺点。RDB是将redis内存中的数据保存为快照文件,恢复速度较快但不支持拉链式快照。AOF是将操作日志保存到磁盘,实时存储数据但恢复速度较慢。文章详细分析了两种机制的优缺点,帮助读者更好地理解redis的持久化存储策略。 ... [详细]
  • 本文介绍了在Linux下安装Perl的步骤,并提供了一个简单的Perl程序示例。同时,还展示了运行该程序的结果。 ... [详细]
  • 本文介绍了在Mac上搭建php环境后无法使用localhost连接mysql的问题,并通过将localhost替换为127.0.0.1或本机IP解决了该问题。文章解释了localhost和127.0.0.1的区别,指出了使用socket方式连接导致连接失败的原因。此外,还提供了相关链接供读者深入了解。 ... [详细]
  • Webmin远程命令执行漏洞复现及防护方法
    本文介绍了Webmin远程命令执行漏洞CVE-2019-15107的漏洞详情和复现方法,同时提供了防护方法。漏洞存在于Webmin的找回密码页面中,攻击者无需权限即可注入命令并执行任意系统命令。文章还提供了相关参考链接和搭建靶场的步骤。此外,还指出了参考链接中的数据包不准确的问题,并解释了漏洞触发的条件。最后,给出了防护方法以避免受到该漏洞的攻击。 ... [详细]
  • 如何去除Win7快捷方式的箭头
    本文介绍了如何去除Win7快捷方式的箭头的方法,通过生成一个透明的ico图标并将其命名为Empty.ico,将图标复制到windows目录下,并导入注册表,即可去除箭头。这样做可以改善默认快捷方式的外观,提升桌面整洁度。 ... [详细]
  • 本文介绍了adg架构设置在企业数据治理中的应用。随着信息技术的发展,企业IT系统的快速发展使得数据成为企业业务增长的新动力,但同时也带来了数据冗余、数据难发现、效率低下、资源消耗等问题。本文讨论了企业面临的几类尖锐问题,并提出了解决方案,包括确保库表结构与系统测试版本一致、避免数据冗余、快速定位问题等。此外,本文还探讨了adg架构在大版本升级、上云服务和微服务治理方面的应用。通过本文的介绍,读者可以了解到adg架构设置的重要性及其在企业数据治理中的应用。 ... [详细]
  • 信息安全等级保护是指对国家秘密信息、法人和其他组织及公民的专有信息以及公开信息和存储、传输、处理这些信息的信息系统分等级实行安全保护,对信息系统中使用的信息安全产品实 ... [详细]
  • 无线认证设置故障排除方法及注意事项
    本文介绍了解决无线认证设置故障的方法和注意事项,包括检查无线路由器工作状态、关闭手机休眠状态下的网络设置、重启路由器、更改认证类型、恢复出厂设置和手机网络设置等。通过这些方法,可以解决无线认证设置可能出现的问题,确保无线网络正常连接和上网。同时,还提供了一些注意事项,以便用户在进行无线认证设置时能够正确操作。 ... [详细]
author-avatar
手机用户2502860763
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有