IPC--信号(2)--信号的发送 & 信号函数的安装

来源:互联网 发布:田岛美工刀片 编辑:程序博客网 时间:2024/05/21 00:54

信号的发送

1、kill()
#include <sys/types.h>
#include <signal.h> 


int kill(pid_t pid,int signo)

pid:是进程id


参 数pid的值

信号的接收进程

pid>0

进程ID为pid的进程

pid=0

同一个进程组的进程

pid<0 pid!=-1

进程组ID为 -pid的所有进程

pid=-1

除 发送进程自身外,所有进程ID大于1的进程

Sinno是信号值

    当为0时(即空信号),实际不发送任何信号,但照常进行错误检查,因此,可用于检查目标进程是否存在,以及当前进程是否具有向目标发送信号的权限(root权限的进程可以向任何进程发送信号,非root权限的进程只能向属于同一个session或者同一个用户的进程 发送信号)。

注意:

Kill()最常用于pid>0时的信号发送,调用成功返回 0; 否则,返回 -1。 注:对于pid<0时的情况,对于哪些进程将接受信号,各种版本说法不一,其实很简单,参阅内核源码kernal/signal.c即可,上表中的 规则是参考red hat 7.2。

2、raise()
#include <signal.h>
int raise(int signo)
向进程本身发送信号,参数为即将发送的信号值。调用成功返回 0;否则,返回 -1。 

3、sigqueue()
#include <sys/types.h>
#include <signal.h> 


int sigqueue(pid_t pid, int signo, const union sigval val)
调用成功返回 0;否则,返回 -1。

注意:

A. 支持信号带有参数,与函数 sigaction()配合使用;

B. union sigval结构

         typedef union sigval 

         {

      int sival_int;

      void *sival_ptr;

}sigval_t;

C. sigqueue()发送非实时信号时,仍然不支持排队,即在信号处理函数执行过程中到来的所有相同信号,都被合并为一个信号。


4. alarm()
#include <unistd.h>
unsigned int alarm(unsigned int seconds)
专门为SIGALRM信号而设,在指定的时间seconds秒后,将向进程本身发送SIGALRM信号,又称为闹钟时间。进程调用alarm后,任何以前 的alarm()调用都将无效。如果参数seconds为零,那么进程内将不再包含任何闹钟时间。
返回值,如果调用alarm()前,进程中已经设置了闹钟时间,则返回上一个闹钟时间的剩余时间,否则返回0。


5、setitimer()
#include <sys/time.h>
int setitimer(int which, const struct itimerval *value, struct itimerval*ovalue));
setitimer()比alarm功能强大,支持3种类型的定时器:

  • ITIMER_REAL: 设定绝对时间;经过指定的时间后,内核将发送SIGALRM信号给本进程;
  • ITIMER_VIRTUAL 设定程序执行时间;经过指定的时间后,内核将发送SIGVTALRM信号给本进程;
  • ITIMER_PROF 设定进程执行以及内核因本进程而消耗的时间和,经过指定的时间后,内核将发送ITIMER_VIRTUAL信号给本进程;

Setitimer()第一个参数which指定定时器类型(上面三种之一);第二个参数是结构itimerval的一个实例,结构 itimerval形式见附录1。第三个参数可不做处理。

Setitimer()调用成功返回0,否则返回-1。


6. abort()
#include <stdlib.h>
void abort(void);

向进程发送SIGABORT信号,默认情况下进程会异常退出,当然可定义自己的信号处理函数。即使SIGABORT被进程设置为阻塞信号,调用abort()后,SIGABORT仍然能被进程接收。该函数无返回值。


信号的安装(设置信号关联动作)

linux主要有两个函数实现信号的安装:signal()、sigaction()

其中signal()在可靠信号系统调用的基础上实现, 是库函数。它只有两个参数,不支持信号传递信息,主要是用于前32种非实时信号的安装;

而sigaction()是较新的函数(由两个系统调用实现:sys_signal以及sys_rt_sigaction),有三个参数,支持信号传递信息,主要用来与 sigqueue() 系统调用配合使用,当然,sigaction()同样支持非实时信号的安装。sigaction()优于signal()主要体现在支持信号带有参数。

1、signal函数
#include <signal.h>
void (*signal(int signum, void (*handler))(int)))(int);
如果该函数原型不容易理解的话,可以参考下面的分解方式来理解:
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler)); 


第一个参数指定信号的值,第二个参数指定针对前面信号值的处理,可以忽略该信号(参数设为SIG_IGN);可以采用系统默认方式处理信号(参数设为 SIG_DFL);也可以自己实现处理方式(参数指定一个函数地址)。
如果signal()调用成功,返回最后一次为安装信号signum而调用signal()时的handler值;失败则返回SIG_ERR。


2、sigaction函数
#include <signal.h>
int sigaction(int signum,const struct sigaction *act, struct sigaction*oldact));

sigaction函数用于改变进程接收到特定信号后的行为。

第一个参数为信号的值,可以为除SIGKILL及SIGSTOP 外的任何一个特定有效的信号(为这两个信号定义自己的处理函数,将导致信号安装错误)。

第二个参数是指向结构sigaction的一个实例的指针,在结构 sigaction的实例中,指定了对特定信号的处理,可以为空,进程会以缺省方式对信号处理;

第三个参数oldact指向的对象用来保存原来对相应信号 的处理,可指定oldact为NULL。

如果把第二、第三个参数都设为NULL,那么该函数可用于检查信号的有效性。

第二个参数最为重要,其中包含了对指定信号的处理、信号所传递的信息、信号处理函数执行过程中应屏蔽掉哪些函数等等。

sigaction结构定义如下:

         struct sigaction 
         {                union                {
                        __sighandler_t   _sa_handler;
                       void (*_sa_sigaction)(int, struct siginfo *,  void *);
                 }_u
                 sigset_t sa_mask;
                 unsigned long sa_flags; 
                 void (*sa_restorer)(void);// 已过时,POSIX不支持它,不应再被使用
        };
                                      

注意:

A、联合数据结构中的两个元素_sa_handler以及*_sa_sigaction指定信号关联函数,即用户指定的信号处理函数。

     除了可以是用户自定义的处理函数外,还可以为SIG_DFL或SIG_IGN。

B、由_sa_handler指定的处理函数只有一个参数,即信号值,所以信号不能传递除信号值之外的任何信息;

     由 _sa_sigaction是指定的信号处理函数带有三个参数,是为实时信号而设的(当然同样支持非实时信号),它指定一个3参数信号处理函数。

         第一个参数为信号值;

         第三个参数没有使用(posix没有规范使用该参数的标准);

         第二个参数是指向siginfo_t结构的指针,结构中包含信号携带的数据值;

         参数所指向的结构如下:


siginfo_t结构中的联合数据成员确保该结构适应所有的信号,比如对于实时信号来说,则实际采用下面的结构形式:

        typedef struct {
               int si_signo;
               int si_errno;                  
               int si_code;                   
               union sigval si_value; 
               } siginfo_t;
               

 

结构的第四个域同样为一个联合数据结构:

        union sigval {
               int sival_int;         
               void *sival_ptr;       
               }

 

采用联合数据结构,说明siginfo_t结构中的si_value要么持有一个4字节的整数值,要么持有一个指针,这就构成了与信号相关的数据。在信号的处理函数中,包含这样的信号相关数据指针,但没有规定具体如何对这些数据进行操作,操作方法应该由程序开发人员根据具体任务事先约定。

前面在讨论系统调用sigqueue发送信号时,sigqueue的第三个参数就是sigval联合数据结构,当调用sigqueue 时,该数据结构中的数据就将拷贝到信号处理函数的第二个参数中。这样,在发送信号同时,就可以让信号传递一些附加信息。信号可以传递信息对程序开发是非常有意义的。

信号参数的传递过程可图示如下:



C、sa_mask指定在信号处理程序执行过程中,哪些信号应当被阻塞。缺省情况下当前信号本身被阻塞,防止信号的嵌套发送,

      除非指定 SA_NODEFER或者SA_NOMASK标志位。

注:请注意sa_mask指定的信号阻塞的前提条件,是在由sigaction()安装信号的处理函数执行过程中由sa_mask指定的信号才被阻塞。

D、sa_flags中包含了许多标志位,包括刚刚提到的SA_NODEFER及SA_NOMASK标志位。另一个比较重要的标志位是 SA_SIGINFO,当设定了该标志位时,表示信号附带的参数可以被传递到信号处理函数中,因此,应该为sigaction结构中的 sa_sigaction指定处理函数,而不应该为sa_handler指定信号处理函数,否则,设置该标志变得毫无意义。即使为sa_sigaction指定了信号处理函数,如果不设置SA_SIGINFO,信号处理函数同样不能得到信号传递过来的数据,在信号处理函数中对这些信息的访问都将导致段错误(Segmentation fault)。

注:很多文献在阐述该标志位时都认为,如果设置了该标志位,就必须定义三参数信号处理函数。实际不是这样的,验证方法很简单:自己实现 一个单一参数信号处理函数,并在程序中设置该标志位,可以察看程序的运行结果。实际上,可以把该标志位看成信号是否传递参数的开关,如果设置该位,则传递 参数;否则,不传递参数。









原创粉丝点击