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

我看可以通过“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. HTTP基础(整理)

    前一段时间看了有关这个协议的相关文档,对这个协议有了新的理解,这里整理一下. http是应用层面向对象的协议. 它有以下几个特点: 1.  支持客户服务器模式(这是废话,不支持这个模式怎么工作) 2. ...

  2. 单点登录系统cas资料汇总

    http://jasig.github.io/cas/4.0.x/index.html           主页 https://jasigcas.herokuapp.com              ...

  3. 【Sprint3冲刺之前】软件开发计划书

    TD校园助手软件开发计划书 1.引言 1.1 编写目的 为了保证项目团队按时保质地完成项目目标,便于项目团队成员更好地了解项目情况,使项目工作开展的各个过程合理有序,同时便于老师和其他同学了解我们的项 ...

  4. java 方法重写原则

    方法重写应遵循“三同一小一大”原则: “三同”:即方法名相同,形参列表相同,返回值类型相同: “一小”:子类方法声明抛出的异常比父类方法声明抛出的异常更小或者相等: “一大”:子类方法的访问修饰符应比 ...

  5. 创业神人&当时钢铁侠:Elon Musk

    Steve Jobs的光环已经随着他的离去而淡褪,短期内,世上恐怕再难有人像他这样惊世骇俗般的改变了世界.但是如果你了解到一个人,一个来自南非年仅40岁的企业家,在短短的20年里,在全世界最酷的三个领 ...

  6. 目标跟踪之粒子滤波---Opencv实现粒子滤波算法

    目标跟踪学习笔记_2(particle filter初探1) 目标跟踪学习笔记_3(particle filter初探2) 前面2篇博客已经提到当粒子数增加时会内存报错,后面又仔细查了下程序,是代码方 ...

  7. ASP.NET动态网站制作(1)--html

    前言:正式上课的第一课,讲的是前端部分的最基础内容:html. 前端:html,css,js 数据库:sql server 动态部分:.net,c#... IIS(Internet Informati ...

  8. GS踢玩家下线功能

    GS踢玩家下线功能 //key:userId, val:nChannelId (当前在线用户) std::map<int, int> m_mapOnLineUserByUid; ///&l ...

  9. EasyDSS RTMP流媒体解决方案之直播录像自动清理方案

    本文转自Marvin的博客: http://blog.csdn.net/marvin1311/article/details/78660592 EasyDSS_Solution直播录像清理 直播录像, ...

  10. ubuntu16.04+cuda8.0+cudnn5.0+caffe

    ubuntu安装过程(硬盘安装)http://www.cnblogs.com/zhbzz2007/p/5493395.html“但是千万不要用麒麟版!!!比原版体验要差很多!!!”开关机的时候电脑最上 ...