2017-04-06

之前在看LinuxThreads线程模型的时候,看到该模型是通过信号实现线程间的同步,当时没有多想,直接当做信号量了,现在想起来真是汗颜……后来想想并不是那么回事,于是,就有了今天这篇博文!

其实关于信号的文章,网上有很多,写的也很好,而笔者仅仅是想把自己的想法记录下来,一来帮助自己捋顺思路,二来说不定还可以帮助他人理解,如有错误,还请指正!


一、总体介绍

Linux的信号在实现机制上根中断很类似,以至于有文献把linux信号作为软中断,这当然在一定程度上是说的过去,但是笔者不建议这么做,因为虽然二者的实现机制又相同之处,但是二者所处理的事务千差万别;况且软中断在Linux中是一个专有名词,比如中断处理下半部常作为软中断的方式存在,而软中断的记录根CPU相关而根某个进程无关,这里强行把信号作为软中断对于初学者的理解也有害无益,至少应该明确说明区别,想当初笔者本科时由老师提到异常就是软中断,这概念因为先入为主,在笔者后来看到真正的软中断时迟迟不能接受。至于软中断,可参考笔者另一篇博文。

  当然把信号作为软中断也并非空穴来风,自然有其道理。首先,二者均是作为异步事件的处理方式。相对于操作系统来讲,中断作为异步方式存在已经众人皆知,信号也不例外。但是信号相对的,是进程,而并非操作系统。说到这里,还有文献说进程其实就是一个虚拟机,当然仅仅是广义上的。那么从这个角度,信号像是把中断扩展到更小的单元上的一个方式。

二、信号的实现方式

  之前说到,信号是相对于进程存在的,在进程结构体task_struct结构中,信号相关的字段如下:

/* signal handlers */
struct signal_struct *signal;
struct sighand_struct *sighand;
/*屏蔽的信号*/
sigset_t blocked, real_blocked;
sigset_t saved_sigmask; /* restored if set_restore_sigmask() was used */
/*挂起的信号*/
struct sigpending pending;

同一个进程中的所有线程共享一个signal_struct结构和sighand_struct结构,即这两个结构都是进程相关的。而之前咱们也说过,Linux中线程在内核中同样也是由task_struct代表的,下面几个字段都是针对单个线程的,blocked字段记录当前线程对信号的屏蔽情况,其类型早期是unsigned long类型,x86架构下就是32位,而现在升级成一个结构,其大小根据具体的信号数量确定,当前最大信号数量为64,以后还可能增加。

#define _NSIG        64
#define _NSIG_BPW 32
#define _NSIG_WORDS (_NSIG / _NSIG_BPW)
#define _NSIG 64
typedef struct {
unsigned long sig[_NSIG_WORDS];
} sigset_t;

利用宏定义扩展位图。每位对应一个信号,当某个信号对应的位为1时,表明该信号暂时被屏蔽,但这并不意味着信号不能产生,恰恰相反,信号依然可以被传递到当前线程,只是当前线程对信号不做处理,把信号阻塞或称为挂起。待到该信号不被屏蔽了,那么该信号就可以得到处理了。挂起的信号记录在pending字段,sigpending结构如下:

struct sigpending {
struct list_head list;
sigset_t signal;
};

所有产生的信号都会被挂入这个list为表头的链表中,这样产生多个信号也不会丢失。signal还是做链表中信号标记,表明当前有哪些类型的信号未处理。链表中节点结构是sigqueue,只是需要注意,这里信号分为可靠信号和不可靠信号。二者的区别暂时不介绍,不可靠信号是早期支持的32个信号,从0-31,而可靠信号是大于31的信号。不可靠信号在投递还是采取先前的方式,这是为了和之前保持兼容,即signal位图中每个位代表一个信号,且一个信号只有一个sigqueue结构与之对应;当目前存在未处理的信号,再次受到这个信号就会忽略。而可靠信号位图中也有与之对应的位,而且每收到一个信号就生成一个sigqueue结构挂入链表,sigqueue结构如下。

struct sigqueue {
struct list_head list;
int flags;
siginfo_t info;
struct user_struct *user;
};

三、信号的处理

信号的处理时机主要由两个:

1、从内核空间返回到用户空间前夕。

2、进程位于内核空间被唤醒。

对于信号的处理主要由两种处理方式,用户可以对信号定义自己的处理函数,也可以采用系统默认的处理函数。在上述task_struct结构中有sighand_struct类型的字段 sighand,记录了信号的处理函数

struct sighand_struct {
atomic_t count;
struct k_sigaction action[_NSIG];
spinlock_t siglock;
wait_queue_head_t signalfd_wqh;
};

第一个count应该指action表项的数目,第二个action和每一个信号对应,一个表项记录一个信号的处理函数,当为0时表示采用系统默认的处理方式。注意,在signal_struct中并没有设置锁,源码注释解释为一个共享的signal_struct必定有一个sighand_struct结构与之对应,对sighand_struct加锁即可实现对signal_struct保护。

struct sigaction {
__sighandler_t sa_handler;
unsigned long sa_flags;
sigset_t sa_mask; /* mask last for extensibility */
};

sa_handler指向一个处理函数,sa_flags记录信号处理时的一些设置,可以有如下值

  • SA_ONSTACK:表明要使用已经注册的新栈,而不是使用进程自身的栈。
  • SA_RESTART:设置在信号被中断后重启。
  • SA_NOCLDSTOP:当该位设置时,在子进程stop时不产生SIGCHLD信号。
  • SA_RESETHAND:当调用信号处理函数时,将信号的处理函数重置为缺省值SIG_DFL
  • SA_RESTART:如果信号中断了进程的某个系统调用,则系统自动启动该系统调用
  • SA_NODEFER :一般情况下, 当信号处理函数运行时,内核将阻塞该给定信号。但是如果设置了 SA_NODEFER标记, 那么在该信号处理函数运行时,内核将不会阻塞该信号

sa_mask就代表在处理当前信号时,可以选择性的屏蔽一些信号。相当于即时有效的。

在用户空间可以调用signal和sigaction 函数给一个信号设置处理函数,SIGKILL和SIGSTOP信号除外,这两个信号必须采用系统默认的函数。当一个信号被设置handler时,已经挂起的该类信号被丢弃。关于signal和sigaction的区别这里不做详细介绍。对于sa_handler还可以是SIG_DFL和SIG_IGN两个值,前者为0表示采取默认的处理方式。后者为1,表示忽略该信号。

参考资料:

LInux3.10.1内核源码

Linux下的信号机制的更多相关文章

  1. linux下 signal信号机制的透彻分析与各种实例讲解

    转自:http://blog.sina.com.cn/s/blog_636a55070101vs2d.html 转自:http://blog.csdn.net/tiany524/article/det ...

  2. 2017-2018-1 20155222 《信息安全系统设计基础》第10周 Linux下的IPC机制

    2017-2018-1 20155222 <信息安全系统设计基础>第10周 Linux下的IPC机制 IPC机制 在linux下的多个进程间的通信机制叫做IPC(Inter-Process ...

  3. Linux下的IPC机制

    Linux下的IPC机制 IPC(Inter-Process Communication)是多个进程之间相互沟通的一种方法.在linux下有多种进程间通信的方法. 共享内存 Linux内存共享有多种, ...

  4. linux下epoll实现机制

    linux下epoll实现机制 原作者:陶辉 链接:http://blog.csdn.net/russell_tao/article/details/7160071 先简单回顾下如何使用C库封装的se ...

  5. linux 下信号处理命令trap && linux下各种信号的意义

    1.用途说明 trap是一个shell内建命令,它用来在脚本中指定信号如何处理.比如,按Ctrl+C会使脚本终止执行,实际上系统发送了SIGINT信号给脚本进程,SIGINT信号的默认处理方式就是退出 ...

  6. linux信号Linux下Signal信号太详细了,终于找到了

    linux信号Linux下Signal信号太详细了,终于找到了 http://www.cppblog.com/sleepwom/archive/2010/12/27/137564.html

  7. linux下六大IPC机制【转】

    转自http://blog.sina.com.cn/s/blog_587c016a0100nfeq.html linux下进程间通信IPC的几种主要手段简介: 管道(Pipe)及有名管道(named ...

  8. Linux 下的同步机制

    2017-03-10 回想下最初的计算机设计,在单个CPU的情况下,同一时刻只能由一个线程(在LInux下为进程)占用CPU,且2.6之前的Linux内核并不支持内核抢占,当进程在系统地址运行时,能打 ...

  9. linux中的信号机制

    概述 Linux信号机制是在应用软件层次上对中断机制的一种模拟,信号提供了一种处理异步事件的方法,例如,终端用户输入中断键(ctrl+c),则会通过信号机制停止一个程序[1]. 这其实就是向那个程序( ...

随机推荐

  1. 基于 jQuery支持移动触摸设备的Lightbox插件

    Swipebox是一款支持桌面.移动触摸手机和平板电脑的jquery Lightbox插件.该lightbox插件支持手机的触摸手势,支持桌面电脑的键盘导航,并且支持视频的播放. 在线预览   源码下 ...

  2. 连接数据库通过配置文件app.config

    ConfigurationManager类 public static class ConfigurationManager 命名空间: System.Configuration 程序集: Syste ...

  3. Python高级编程之生成器(Generator)与coroutine(二):coroutine介绍

    原创作品,转载请注明出处:点我 上一篇文章Python高级编程之生成器(Generator)与coroutine(一):Generator中,我们介绍了什么是Generator,以及写了几个使用Gen ...

  4. 如何对MySQL中的大表进行数据归档

    使用MySQL的过程,经常会遇到一个问题,比如说某张”log”表,用于保存某种记录,随着时间的不断的累积数据,但是只有最新的一段时间的数据是有用的:这个时候会遇到性能和容量的瓶颈,需要将表中的历史数据 ...

  5. IE9 BUG overflow :auto 底部空白解决方案

    今天去升级了到IE9,运行项目的时候发现,我的div显示滚动条时候,用js动态加载进去的内容在光标移动的时候,底部自动被撑大留着空白, IE8 Chrome这些以前都试过 没发现这个问题 研究了好久 ...

  6. 005Maven_Myeclipse和Maven整合

    准备好:1.Myeclipse2014; 2. E盘下面的:

  7. 一个 Map 函数、一个 Reduce 函数和一个 main 函数

    MapReduce 最简单的 MapReduce应用程序至少包含 3 个部分:一个 Map 函数.一个 Reduce 函数和一个 main 函数.main 函数将作业控制和文件输入/输出结合起来.在这 ...

  8. img与特殊布局下对浏览器渲染的剖析

    补白 在内联元素中,分为替换元素和非替换元素(不了解的同学可以百度一下),非替换元素是不可以设置尺寸的,而替换元素作为特殊的内联元素,由于其自身拥有尺寸属性,所以其的尺寸是可以进行再次设置的. 此文适 ...

  9. 使用typescript开发react应用

    初始化 mkdir project-dir cd project-dir yarn init -y 安装依赖 yarn add react react-dom yarn add -D typescri ...

  10. 自动化测试环境准备robotframework

    (一)针对python2.7版本的自动化环境准备: python 下载地址: https://www.python.org/downloads/ 这里选择Python2.7系列的,后面涉及到wxPyt ...