您的位置:首页 > 运维架构 > Linux

linux系统编程之信号(一)

2014-01-12 09:14 363 查看
今天起,开始新的知识的学习,对于上个系列进程的学习还差一个理论上的总结,这个会下次补回来,以便通过实践之后,再用理论将其巩固一下,好了,话不多说,开始进入这个主题的学习----信号,很重要,但不是太容易理解,所以得一步一步来!

[b]中断【纯概念,但是很重要】:[/b]
在学习信号之前,首先需要理解一下什么是中断,因为信号与中断有很多的相似之处,中断,顾名思义就是中途打断:



那什么是异步事件呢?它是没有一定时序关系,随机发生的事件,在中断技术出现之前,计算机对异步事件处理能力是有限的,通过是通过查询的方式来处理的,举一个现实生活中的例子:
比如张三正在看书,这时厨房里又正在烧开水,这时,张三看书时并不知道水是否烧开了,他就需要跑到厨房当中"查询"一下水是否烧开了,然后再回来看书,这时又不放心厨房水是否烧开了,于是又跑进厨房查询,如此循环,直到水烧开之后,他才能够静下心来看自己的书,这就是典型的查询技术
有了中断技术后,又是如何的呢?还是以上面这个例子:
张三在看书的同时,设置一个闹钟,比如说是10分钟的闹钟,当水烧开的时候,会响铃,其中响铃可以看作是一个中断信号,闹钟可以看成是一个中断源,当闹钟响之后,张三就会去处理这一次的中断事件,也就是跑到厨房将煤气给关闭,将开水倒进热水瓶当中,这叫中断执行程序,实际上张三在做这件事之前,需要保护现场,记住当前看到了第几页,当执行完中断执行程序之后,则恢复现场,继续从之前看到的页数开始看书,这就是中断执行的整个流程,总结一下:
  中断源 -> 中断屏蔽 -> 保护现场 -> 中断处理程序 -> 恢复现场
中断处理程序是保存在哪的呢?实际上,它的入口地址是保存在中断向量表当中的,由于计算机中的中断个数是固定的,一般在操作系统启动的时候,会初始化一个中断向量表,它会保存固定个数的中断处理程序入口的地址,这样的话,CPU就可以根据中断号,从中断向量表当中找到对应中断的中断处理程序的入口地址,从而调用处理程序。
对于闹钟这个中断源,产生了一个中断信号,不同的中断源会产生不同的中断信号,再回到这个例子,张三在看书的时候,可能闹钟响的同时,会听到外面有人敲门的中断信号到来,还有可能是电话响起来产生另外一个中断信号,但是对于同时到来的中断,张三可以决定哪个中断先处理,这也就是中断的优先级,他觉得开水烧开的中断优化级最高。另外张三也有可能屏蔽一些不必要的中断,比如说电话响铃了,在看书之时,他觉得这个中断是可以屏蔽的,也就是中断屏蔽.

[b]中断分类:[/b]
[b]

[/b]

比如键盘产生的中断、鼠标产生的中断、打印机产生的中断等,这些都是属于硬件中断。



比如说除0中断、单步执行、对于X86平台来说执行了一个INT指令,从用户空间到内核空间

[b]信号【核心】:[/b]



[b]信号与中断总结:[/b]
[b]

[/b]



[b]信号名称:[/b]
那在linux/unix下,到底有哪些信号呢?我们可以通过一个命令进行查看:



这些信号都有它们不同的涵义:





对于这些信号的默认处理行为(也就是我们可以自定义自己的行为),可以在man手册上查询到:





[b]进程对信号的三种响应:[/b]
当对于到来的信号,可以有三种不同的响应



为什么呢?SIGKILL是杀死进程的信号,它是9号进程:



在shell命令中,我们可以用kill -9 pid来对一个进程进行杀死,如果进程能够忽略9号信号的话,意味着当管理员向进程发送9号信号时,如果进程可以屏蔽不处理的话,那就无法杀死一个非法的进程了,而同理,SIGSTOP是停止一个进程信号,







也就是我们看到的信号对应的action,上面有介绍过:



[b]signal:[/b]

下面来学一下安装信号函数:



下面以代码来进行说明:



编译运行:



按下ctrl+c:





可以信号是一种异步事件的响应,当响应完之后,会还原现场,又回到了for死循环代码上了:



这时按下ctrl+\退出:



实际上,ctrl+\会产生一个退出信号:



由于我们程序没有注册该信号,所以由系统默认处理,既程序结束。
对于signal函数,它的返回值为它注册的上一个信号的处理程序,这样说有点空洞,下面以程序来说明一下:

#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h>

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>

#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)

void handler(int sig);
int main(int argc, char *argv[])
{
__sighandler_t oldhandler;
oldhandler = signal(SIGINT, handler);//这时返回的处理程序是注册handler之前的,也就是系统默认的处理程序
if (oldhandler == SIG_ERR)
ERR_EXIT("signal error");

while (getchar() != '\n')//死循环是为了测试,当按了ctrl+c之后,会不断死循环,直到按了回车键
;
if (signal(SIGINT, oldhandler) == SIG_ERR)//这时,再次注册信号,但是这次是注册成了默认处理程序,而ctrl+c的默认处理就是终止程序
ERR_EXIT("signal error");
for (;;) ;
return 0;
}

void handler(int sig)
{
printf("recv a sig=%d\n", sig);
}


编译运行:



按回车键:



这时,再按ctrl+c:



实际上,恢复默认的处理行为,还可以用它来代替:



编译运行,输出效果一样:



好了,今天的学习先到这,下回见!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: