[APUE]信号

信号是软件中断,是软件层次上对硬件中断的一种模拟,原理上一个进程收到一个信号与cpu收到一个中断请求是一样的。信号提供了一种处理异步事件的方法。

为什么说信号提供了一种处理异步事件的方法:因为一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知道信号到底是什么时候到达。

1、同步和异步

当一个进程A存在一个必须执行的操作点,同时该操作点,与另一个进程B的某个操作点存在因果时序关系,则A相对B为同步。反之为异步。

1)A相对B同步,未必B相对A同步。
2)AB两个进程之间必须存在操作点之间的因果关系,才叫同步。如果说两个进程抢打印机,则不叫同步。虽然可能存在某个操作点上的相互影响。虽然他们之间可能出现阻塞。但和同步异步没联系。
3)A相对B同步,则一定存在一个严格按照(请求,等待,接受)的时序过程进行。这种工作在软件设计时是显式存在的(即你在代码中可以看到)。
4)同步和异步都是相对的。在不同的尺度下,可能产生变化。

2、信号的概念

每个信号都有一个名字,这些名字都以SIG开头。

在头文件<signal.h>中,这些信号都被定义成正整数。不存在编号为0的信号,kill函数对信号编号为0有特殊的应用。

当某个信号出现时,可以要求内核按照下列三种方式之一进行处理(信号的处理):

1)忽略信号(SIG_IGN)。有两种信号(SIGKILL和SIGSTOP)不能被忽略,它们向超级用户提供了使进程终止或停止的可靠方法。

2)捕捉信号(程序员为某信号注册处理函数)。为做到这一点,要通知内核在某种信号发生时调用一个用户函数。不能捕捉SIGKILL和SIGSTOP信号。

3)执行系统默认动作(SIG_DFL)。针对大多数信号的系统默认动作是终止进程。

3、函数signal

#include<signal.h>
void (*signal(int signo,void (*func)(int)))(int);
                        //signo:信号名;func:信号处理函数。当进程收到signo信号,就会异步调用func函数
                        //返回值:若成功,返回以前的信号处理配置;若出错,返回SIG_ERR

从signal函数的限制:不改变信号的处理方式就不能确定信号的当前处理方式。

进程创建:当一个进程调用fork,其子进程继承父进程的信号处理方式。

4、可重入函数

可重入函数主要用于多任务环境中,一个可重入的函数简单来说就是可以被中断的函数,也就是说,可以在这个函数执行的任何时刻中断它,转入OS调度下去执行另外一段代码,而返回控制时不会出现什么错误;而不可重入的函数由于使用了一些系统资源,比如全局变量区,中断向量表等,所以它如果被中断的话,可能会出现问题,这类函数是不能运行在多任务环境下的。

满足下列条件的函数多数是不可重入的:
1) 函数体内使用了静态的数据结构;
2) 函数体内调用了malloc()或者free()函数;
3) 函数体内调用了标准I/O函数。
若在信号处理程序中调用一个非可重入函数,则其结果是不可预知的。
5、函数kill,raise,alarm,pause
kill函数将信号发送给进程或进程组,raise函数则允许进程向自身发送信号。
int kill(pid_t pid,int signo);
int raise(int sugno);
                           //两个函数返回值:若成功,返回0;若出错,返回-1

调用raise(sig)等价于调用kill(getpid(), sig)。

kill的pid参数有四种可能:

pid > 0: 将该信号发送给进程ID为pid的进程

pid == 0: 将该信号发送给与发送进程属于同一进程组的所有进程,而且发送进程具有向这些进程发送信号的权限。

pid < 0: 将该信号发送给其进程组ID等于pid的绝对值,而且发送进程具有向其发送信号的权限。

pid == -1:将该信号发送给发送进程有权限向他们发送信号的系统上的所有进程。

使用alarm函数可以设置一个计时器,在将来某个指定的时间该计时器会超时。当计时器超时时,产生SIGALRM信号。如果不忽略或不捕捉此信号,则其默认动作是终止调用该alarm函数的进程。每个进程只能有一个闹钟时间。

unsigned int alarm(unsigned int seconds);
                                //返回值:0或者以前设置的时钟的余留秒数

pause函数使调用进程挂起直到捕捉到一个信号。只有执行了一个信号处理程序并从其返回时,pause才返回。在这种情况下,pause返回-1,并将errno设置为EINTR。

int pause(void);
          //返回值:-1,erron设置为EINTR

6、信号集

需要一个类型来表示信号的集合,以便某些函数运用这种数据结构。但是信号的数目一般比整形的位数大(当然只要可能大,在设计的时候就必须考虑),所以系统定义了sigset_t类型来表示信号集,以下是处理信号集的一些函数:

intsigemptyset(sigset_t *set);                  //初始化信号集,清除所有信号
intsigfillset(sigset_t *set);                   //初始化信号集,使其包含所有信号  
intsigaddset(sigset_t *set, int signum);        //增加信号signo  
intsigdelset(sigset_t *set, int signum);        //清除信号signo  
                         //4个函数返回值:成功返回0,出错返回-1
intsigismember(const sigset_t *set, int signum);    //判定信号signo是否属于信号集set  
                         //返回值:若真返回1,假返回0,出错返回-1  

 

posted on 2015-07-16 09:45  kona  阅读(254)  评论(0编辑  收藏  举报