信号(或软中断)是在软件层次上对中断的一个模拟,其运行在“用户空间”,一个进程对另外一个或几个进程通过发送信号来实现异步通信。当接收进程接收到信号后,其可以注册一下处理函数来说对这些信号进行处理(也可以选择忽略该信号或者采用系统默认的处理方式)。 

我看可以通过“kill -l”命令来查看系统支持的信号,比如SIGKILL它表示需要终止一个进程,它有一个系统特定的信号值9。这些值都定义在signal.h中 

在signal.h中有个叫做_NSIG(一般为64)的宏其表示该系统支持的最多信号数,而SIGRTMAX (_NSIG-)则表示信号的最大值,而与之相对应的SIGRTMIN却不是表示信号的最小值,其表示可靠信号的最小值。按照信号的可靠性,可将信号分为“可靠信号(实时信号)”和“不可靠信号(非实时信号)”,以SIGRTMIN为界限,值小于SIGRTMIN的信号为不可靠信号,其继承于早期的UNIX系统,SIGRTMIN到SIGRTMAX之间的为可靠信号。 

不可靠信号有两点需要注意:一是其在执行完自定义信号处理函数后会将信号处理方式重置为系统默认方式(如果要多次处理同一个信号,则要多次按照信号处理函数,不过好像后期的Linux对这点做了改进而无需重新安装)。二是不可靠信号不支持排队,其有可能会出现信号丢失的情况。 

假设进程A向进程B发送信号,那么一个很简单的信号流程是:进程A调用某个函数产生某个信号,信号被发送到进程B,然后进程B对该信号进行处理。 

信号的产生和发送: 

我们用结构 

typedef struct siginfo {
//… some info of a signal
} signifo_t;
来表示一个信号的相关信息(比如信号值,由谁发送的等等)
用结构
struct sigqueue {
struct sigqueue *next;
siginfo_t info;
};
来表示有n个siginfo_t结构构成的队列。
再假设有这样一个数据结构:
typedef struct {
unsigned long sig[_NSIG_WORDS];
} sigset_t; 其中_NSIG_WORDS表示的值一般为2。我们知道long为32位,那么sig[_NSIG_WORDS]则为64位,其可以表示64个位的集合,很容易地就可以将集合中的元素其映射到_NSIG个信号上:如果该位为1则表示有着对应值的信号在该集合中。(实际上,因为没有0号信号,所以信号值和位值刚好错开一位) OK,我们如何用这些数据结构来表示哪些信号被发送到了某个进程呢,很简单地,如果进程描述符(PCB)中有sigset_t类型的字段的话,将该字段的对应位置1就可以了,而这些信号的详细信息如果能被保存在sigqueue类型的字段中的话就更完美了。实际上PCB就是这么做的,只不过其将sigset_t和sigqueue合并成了一个称为sigpending的结构: struct sigpending {
struct sigqueue *head, **tail;
sigset_t signal;
}; 所以PCB的sigpending字段表示被发送到了该进程但还没有来得及处理的信号(被挂起的)。 从这里我们可以看出,所谓的“信号的产生”实际上就是某个进程请求内核去将另外一个进程的sigpending字段设置成相应的值罢了,并将相应的其他信息插入到sigqueue队列中。 设置sigpending时有一个比较有意思的地方:我们知道,可以用sigset_t中的某一个位表示对应的信号是否被发送到了该进程,其只能简单地表示“有()”或“无()”,如果某个信号被发送了多次的话,则无法在sigset_t中体现,但可以通过sigqueue队列来体现,实际上,对应不可靠信号(值小于SIGRTMIN的),当设置sigpending时,如果内核发现signal_t相应位已经被置1的话,则内核会丢弃该信号,这也就是为什么说该信号是“不可靠的”;而对应可靠信号,即便signal_t相应位已经被置1,此次信号的相关数据(siginfo_t)仍然会被包装成一个队列节点而被插入到队列中去。 另外从编程角度,要发送一个信号,一般调用下面这些函数: int kill(pid_t pid, int sig);
向进程或进程组发送某个信号
int raise(int sig);
向调用进程自身发信号
int sigqueue(pid_t pid, int signo, const union sigval value);
与kill类似,但多了一些附加信息
unsigned alarm(unsigned seconds);
指定的时间后向调用进程发送一个SIGALRM信号(闹钟信号)
int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue);
与alarm类似同样发送SIGALRM信号,但支持更详细的设定,更多参考这里
void abort(void);
向进程发送SIGABRT信号,让进程中止。 信号的接收和处理 进程从内核态返回用户态时,内核会去检查是否有信号被发送到了该进程,如果有,则让该进程该处理该信号。进程对于一个信号可以有三种处理方式: ,显示地忽略该信号: 其中SIGKILL和SIGSTOP是不能被忽略的,其必须采用下面的第2种方式。 ,采用系统默认的处理方式进行处理: 默认处理可以有这么几种方式:abort; abort并dump; ignore; stop(暂停进程,置为 TASK_STOPPED状态); continue(继续执行,与stop向对应,置为TASK_RUNNING状态) ,调用进程注册的信号处理函数进行自定义处理。 当处理完毕后,内核会改变sigpending中的相关值以表示处理完毕了(比如将sigset_t相应位置0,从sigqueue中删除相关元素等) 至于如何注册信号处理函数,Linux有下面两种方式: typedef void (*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler); 调用signal()函数,其表示从现在开始我关心那些信号值为signum的信号,如果该信号发送到笨进程的话,请调用handler去处理它。其主要用于非可靠信号。 比如下面这个DEMO,去自定义处理SIGINT (按ctrl-c终止进程时会发送该信号) #include <stdio.h>
#include <signal.h>
#include <unistd.h> #define SIG2CATCH 2 void handler(int signum)
{
if(signum == SIG2CATCH)
{
printf("you wanna kill me with ctrl-c ?! no way\n");
}
} int main()
{
printf("app start ...\n"); signal(SIG2CATCH, handler); while()
{
sleep();
} printf("app end ...\n"); return ;
} 运行程序,并试图用ctrl-c结束程序后,SIGINT(值为2)信号会发送到该进程,默认情况下其会终止程序,但这里采用了自定义处理函数,其仅仅打印出一段文字(可以用ctrl-z结束) 另一个注册方法是: int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact); 其主要用于可靠信号,并且可以在sigaction中包含附件信息。详细的参考这里。

linux 进程学习笔记-进程信号sigal的更多相关文章

  1. linux 进程学习笔记-进程跟踪

    进程跟踪 long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data); Linux用ptrace来进行进 ...

  2. linux 进程学习笔记-进程ID,PID

    PID,进程号 , 范围在2~(??为什么需要这么多),而一个名为idle (或swapper)的进程占据的编号0,init进程占据了编号1. 进程0和进程1 : 系统启动时会从无到有地创建进程0,它 ...

  3. linux c学习笔记----进程创建(fork,wait,waitpid)

    1.pid_t fork(); (1)当一个进程调用了fork 以后,系统会创建一个子进程.这个子进程和父进程不同的地方只有他的进程ID 和父进程ID,其他的都是一样.就象符进程克隆(clone)自己 ...

  4. linux 进程学习笔记-进程退出/终止进程

    <!--[if !supportLists]-->Ÿ <!--[endif]-->退出/终止进程 void _exit(int status) 与 void exit(int ...

  5. linux 进程学习笔记-进程pipe管道

    所谓“进程间通信(IPC,inter-process communication)”,按照其目的讲就是让进程之间能够“共享数据”,“传输数据”,“事件通知”等,我所知道的一共有“管道” “信号” “消 ...

  6. Linux内核学习笔记-2.进程管理

    原创文章,转载请注明:Linux内核学习笔记-2.进程管理) By Lucio.Yang 部分内容来自:Linux Kernel Development(Third Edition),Robert L ...

  7. Linux内核学习笔记二——进程

    Linux内核学习笔记二——进程   一 进程与线程 进程就是处于执行期的程序,包含了独立地址空间,多个执行线程等资源. 线程是进程中活动的对象,每个线程都拥有独立的程序计数器.进程栈和一组进程寄存器 ...

  8. 操作系统学习笔记----进程/线程模型----Coursera课程笔记

    操作系统学习笔记----进程/线程模型----Coursera课程笔记 进程/线程模型 0. 概述 0.1 进程模型 多道程序设计 进程的概念.进程控制块 进程状态及转换.进程队列 进程控制----进 ...

  9. JUC学习笔记——进程与线程

    JUC学习笔记--进程与线程 在本系列内容中我们会对JUC做一个系统的学习,本片将会介绍JUC的进程与线程部分 我们会分为以下几部分进行介绍: 进程与线程 并发与并行 同步与异步 线程详解 进程与线程 ...

随机推荐

  1. win2012R2无法打开匿名级安全令牌

    解决办法:  1.输入“dcomcnfg.exe”,打开组件服务管理. 2.展开组件服务,计算机,右击我的电脑,选择属性. 3.在默认属性选项卡中,      选择:- 勾选“在此计算机中启用分布式C ...

  2. Vue.js 很好,但会比 Angular 或 React 更好吗?

    文章转自:http://www.oschina.net/translate/vuejs-is-good-but-is-it-better-than-angular-or-rea Vue.js 是一个用 ...

  3. POJ 1698 Alice&#39;s Chance(最大流+拆点)

    POJ 1698 Alice's Chance 题目链接 题意:拍n部电影.每部电影要在前w星期完毕,而且一周仅仅有一些天是能够拍的,每部电影有个须要的总时间,问能否拍完电影 思路:源点向每部电影连边 ...

  4. 记一次Oracle数据故障排除过程

    前天在Oracle生产环境中,自己的存储过程运行时间超过1小时,怀疑是其他job运行时间过长推迟了自己job运行时间,遂重新跑job,发现同测试环境的确不同,运行了25分钟. 之后准备在测试环境中制造 ...

  5. 1-2:CSS3课程入门之结构选择

    E:nth-child(n) 表示E父元素中的第n个字节点 p:nth-child(odd){background:red}/*匹配奇数行*/ p:nth-child(even){background ...

  6. firfox浏览器常用快捷键

    Ctrl + 数字键来打开第N个标签页这种还要先数完再到键盘上找数字Ctrl + Page Up = 激活左边一个标签页Ctrl + Page Down = 激活右边一个标签页Ctrl + Tab = ...

  7. JavaScript之this的工作原理

    JavaScript 有一套完全不同于其它语言的对 this 的处理机制. 在五种不同的情况下 ,this 指向的各不相同. 1.全局范围内 当在全部范围内使用 this,它将会指向全局对象. 2.函 ...

  8. PowerBuilder -- 指定重复的列不显示

  9. Echache整合Spring缓存实例解说

    林炳文Evankaka原创作品.转载请注明出处http://blog.csdn.net/evankaka 摘要:本文主要介绍了EhCache,并通过整合Spring给出了一个使用实例. 一.EhCac ...

  10. l两张图片轮播

    在head里面加 <script language="javascript"> function scroll(spanlevel) { if (spanlevel.s ...