Chinaunix首页 | 论坛 | 博客
  • 博客访问: 214469
  • 博文数量: 72
  • 博客积分: 3890
  • 博客等级: 中校
  • 技术积分: 810
  • 用 户 组: 普通用户
  • 注册时间: 2009-01-05 20:00
文章分类

全部博文(72)

文章存档

2010年(20)

2009年(52)

我的朋友

分类: LINUX

2010-08-17 20:20:12

转载:出处

作者 大漠孤星.

有任何意见和建议请mail to :diablo_tina@163.com或者QQ 6299745联系.

一,前言


信号是进程之间互传消息的一种方法俗称软件中断。很多比较重要的应用程序都需处理信号。信号提供了一种

处理异步事件的方法:终端用户键入中断键,则会通过信号机构停止一个程序。所以,信号可以说是进程控制的一部分。

在SCO openserver 5.05上 kill -l得到

CODE

         HUP SYS STOP
         INT PIPE TSTP
         QUIT ALRM CONT
         ILL TERM TTIN
         TRAP USR1 TTOU
         ABRT USR2 VTALRM
         EMT CHLD PROF
         FPE PWR XCPU
         KILL WINCH XFSZ
         BUS URG
         SEGV POLL

   

在Aix 4.3上kill -l 得到
CODE

       1) HUP        14) ALRM       27) MSG        40) bad trap   53) bad trap
       2) INT        15) TERM       28) WINCH      41) bad trap   54) bad trap
       3) QUIT       16) URG        29) PWR        42) bad trap   55) bad trap
       4) ILL        17) STOP       30) USR1       43) bad trap   56) bad trap
       5) TRAP       18) TSTP       31) USR2       44) bad trap   57) bad trap
       6) ABRT       19) CONT       32) PROF       45) bad trap   58) bad trap
       7) EMT        20) CHLD       33) DANGER     46) bad trap   59) CPUFAIL
       8) FPE        21) TTIN       34) VTALRM     47) bad trap   60) GRANT
       9) KILL       22) TTOU       35) MIGRATE    48) bad trap   61) RETRACT
      10) BUS        23) IO         36) PRE        49) bad trap   62) SOUND
      11) SEGV       24) XCPU       37) bad trap   50) bad trap   63) SAK
      12) SYS        25) XFSZ       38) bad trap   51) bad trap
      13) PIPE       26) bad trap   39) bad trap   52) bad trap

   


在Redhat 7.3上kill -l 得到
CODE

        1) SIGHUP        2) SIGINT        3) SIGQUIT       4) SIGILL
        5) SIGTRAP       6) SIGABRT       7) SIGBUS        8) SIGFPE
        9) SIGKILL      10) SIGUSR1      11) SIGSEGV      12) SIGUSR2
       13) SIGPIPE      14) SIGALRM      15) SIGTERM      17) SIGCHLD
       18) SIGCONT      19) SIGSTOP      20) SIGTSTP      21) SIGTTIN
       22) SIGTTOU      23) SIGURG       24) SIGXCPU      25) SIGXFSZ
       26) SIGVTALRM    27) SIGPROF      28) SIGWINCH     29) SIGIO
       30) SIGPWR       31) SIGSYS       32) SIGRTMIN     33) SIGRTMIN+1
       34) SIGRTMIN+2   35) SIGRTMIN+3   36) SIGRTMIN+4   37) SIGRTMIN+5
       38) SIGRTMIN+6   39) SIGRTMIN+7   40) SIGRTMIN+8   41) SIGRTMIN+9
       42) SIGRTMIN+10 43) SIGRTMIN+11 44) SIGRTMIN+12 45) SIGRTMIN+13
       46) SIGRTMIN+14 47) SIGRTMIN+15 48) SIGRTMAX-15 49) SIGRTMAX-14
       50) SIGRTMAX-13 51) SIGRTMAX-12 52) SIGRTMAX-11 53) SIGRTMAX-10
       54) SIGRTMAX-9   55) SIGRTMAX-8   56) SIGRTMAX-7   57) SIGRTMAX-6
       58) SIGRTMAX-5   59) SIGRTMAX-4   60) SIGRTMAX-3   61) SIGRTMAX-2
       62) SIGRTMAX-1   63) SIGRTMAX
   

能看出明显差距。


信号出现在 UNIX 的早期版本中 ,但早期的信号模型是不可靠的, 信号可能被丢失,
也很难处理关键段。UNIX 的两个重要分支 BSD 和 System V 分别对早期的信号进行了扩展 ,
但这两个系统的扩展并不兼容POSIX 统一了这两种实现, 最终提供了可靠的信号模型。


信号的产生条件

. 当用户按某些终端键时,产生信号。
. 硬件异常产生信号:除数为0、无效的存储访问等等。
. 进程用kill函数可将信号发送给另一个进程或进程组
. 用户可用kill命令将信号发送给其他进程
. 当检测到某种软件条件已经发生,并将其通知有关进程时也产生信号。

接到信号的处理办法

1,忽略,但是SIGKILL和SIGSTOP不能忽略。
2,捕捉。
3,执行系统的默认处理。

信号的分类

非可靠信号:早期unix下的不可靠信号主要指的是进程可能对信号做出错误的反应
以及信号可能丢失。
可靠信号: 信号值位于SIGRTMIN和SIGRTMAX之间的信号都是可靠信号,可靠信号
克服了信号可能丢失的问题。

信号的可靠与不可靠只与信号值有关,与信号的发送及安装函数无关。

当然也可以称为实时信号或者非实时信号,非实时信号都不支持排队,都是不可靠信号;

实时信号都支持排队,都是可靠信号。


这里我说一下信号的生命周期,对于理解信号的分类有很大的帮助。


从信号发送到信号处理函数的执行完毕。对于一个完整的信号生命周期(从信号发送到相应的处

理函数执行完毕)来说,可以分为三个重要的阶段,这三个阶段由四个重要事件来刻画:信号产生;

信号在进程中注册完毕;信号在进程中的注销完毕;信号处理函数执行完毕。相邻两个事件的时间

间隔构成信号生命周期的一个阶段。

当一个实时信号发送给一个进程时,不管该信号是否已经在进程中注册,都会被再注册一次,因此,

信号不会丢失,因此,实时信号又叫做"可靠信号"。这意味着同一个实时信号可以在同一个进程的未决

信号信息链中占有多个sigqueue结构(进程每收到一个实时信号,都会为它分配一个结构来登记该信号信息,

并把该结构添加在未决信号链尾,即所有诞生的实时信号都会在目标进程中注册);

当一个非实时信号发送给一个进程时,如果该信号已经在进程中注册,则该信号将被丢弃,造成信号丢失。

因此,非实时信号又叫做"不可靠信号"。这意味着同一个非实时信号在进程的未决信号信息链中,至多占有一个

sigqueue结构(一个非实时信号产生后,1如果发现相同的信号已经在目标结构中注册,则不再注册,对于

进程来说,相当于不知道本次信号发生,信号丢失;2如果进程的未决信号中没有相同信号,则在进程中注册自己)。

需要注意的要点是:

1)信号注册与否,与发送信号的函数(如kill()或sigqueue()等)以及信号安装函数(signal()及sigaction())无关,

只与信号值有关(信号值小于SIGRTMIN的信号最多只注册一次,信号值在SIGRTMIN及SIGRTMAX之间的信号,只要被进程接收到就被注册)。

2)在信号被注销到相应的信号处理函数执行完毕这段时间内,如果进程又收到同一信号多次,则对实时信号来说,

每一次都会在进程中注册;而对于非实时信号来说,无论收到多少次信号,都会视为只收到一个信号,只在进程中注册一次。

当然还有有些需要知道的概念比如 低速系统调用,中断系统调用,可重入函数等等概念,请查阅环境高级编程。

一些概念性的东西大概就这些东西吧,下面就分类介绍一下具体的函数。

我下面介绍的,如果没有特殊说明,都是对于linux上的实现,当然都是符合POSIX标准喽。



二,函数详细介绍



A:信号的发送

发送信号的主要函数有:kill(),raise(),sigqueue(),alarm(),setitimer()以及abort()。



1, int kill(pid_t pid,int signo)

用到的头文件:
#include
#include


参数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。



2, int raise(int signo)
用到的头文件:
#include

向进程本身发送信号,参数为即将发送的信号值。调用成功返回 0;否则,返回 -1。

3, int sigqueue(pid_t pid, int sig, const union sigval val)

用到的头文件:
#include
#include

调用成功返回 0;否则,返回 -1。

sigqueue()是比较新的发送信号系统调用,主要是针对实时信号提出的(当然也支持前32种),支持信号带有参数,

与函数sigaction()配合使用。sco 5.05没有此函数。

sigqueue的第一个参数是指定接收信号的进程ID,第二个参数确定即将发送的信号,第三个参数是一个联合数据结构union sigval,

指定了信号传递的参数,即通常所说的4字节值。

typedef union sigval
{
int sival_int;
void *sival_ptr;
}sigval_t;


sigqueue()比kill()传递了更多的附加信息,但sigqueue()只能向一个进程发送信号,而不能发送信号给一个进程组。如果sig为0,

将会执行错误检查,但实际上不发送任何信号,0值信号可用于检查pid的有效性以及当前进程是否有权限向目标进程发送信号。

在调用sigqueue时,sigval_t指定的信息会拷贝到3参数信号处理函数(3参数信号处理函数指的是信号处理函数由sigaction安装,

并设定了sa_sigaction指针,稍后将阐述)的siginfo_t结构中,这样信号处理函数就可以处理这些信息了。由于sigqueue系统调用

支持发送带参数信号,所以比kill()系统调用的功能要灵活和强大得多。

注:sigqueue()发送非实时信号时,第三个参数包含的信息仍然能够传递给信号处理函数; sigqueue()发送非实时信号时,仍然不支持排队,

即在信号处理函数执行过程中到来的所有相同信号,都被合并为一个信号。


4, unsigned int alarm(unsigned int seconds)

用到的头文件:

#include

专门为SIGALRM信号而设,在指定的时间seconds秒后,将向进程本身发送SIGALRM信号,又称为闹钟时间。进程调用alarm后,

任何以前的alarm()调用都将无效。如果参数seconds为零,那么进程内将不再包含任何闹钟时间。

函数返回是这样的,如果调用alarm()前,进程中已经设置了闹钟时间,则返回上一个闹钟时间的剩余时间,否则返回0。

5, int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue));

用到的头文件:
#include

setitimer()比alarm功能强大,支持3种类型的定时器:

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

Setitimer()第一个参数which指定定时器类型(上面三种之一);第二个参数是结构itimerval的一个实例,结构itimerval。

结构itimerval:

struct itimerval
{
struct timeval it_interval; /* next value */
struct timeval it_value; /* current value */
};

struct timeval
{
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
};




第三个参数可不做处理。

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


这是我man setitimer时看到的

getitimer and setitimer are not part of any currently supported standard;
they were developed at the University of California at Berkeley and are
used by permission.


6, void abort(void);

用到的头文件:
#include


向进程发送SIGABORT信号,默认情况下进程会异常退出,当然可定义自己的信号处理函数。

即使SIGABORT被进程设置为阻塞信号,调用abort()后,SIGABORT仍然能被进程接收。该函数无返回值。




B:信号的捕获与安装(设置信号关联动作)



如果进程要处理某一信号,那么就要在进程中安装该信号。安装信号主要用来确定信号值及进程针对该信号值

的动作之间的映射关系,

即进程将要处理哪个信号;该信号被传递给进程时,将执行何种操作。

linux主要有两个函数实现信号的安装:signal()、sigaction()。其中signal()在可靠信号系统调用的基础上实现,

是库函数。它只有两个参数,不支持信号传递信息,主要是用于前32种非实时信号的安装;而sigaction()是较新的

函数(由两个系统调用实现:sys_signal以及sys_rt_sigaction),有三个参数,支持信号传递信息,主要用来与

sigqueue() 系统调用配合使用,当然,sigaction()同样支持非实时信号的安装。sigaction()优于signal()主要体现

在支持信号带有参数。

1, void (*signal(int signum, void (*handler))(int)))(int);

#include

如果该函数原型不容易理解的话,可以参考下面的分解方式来理解:

typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler));

第一个参数指定信号的值,第二个参数指定针对前面信号值的处理,

a,可以忽略该信号(参数设为SIG_IGN);

b,可以采用系统默认方式处理信号(参数设为SIG_DFL);

b,也可以自己实现处理方式(参数指定一个函数地址)。

如果signal()调用成功,返回最后一次为安装信号signum而调用signal()时的handler值;失败则返回SIG_ERR。

2, int sigaction(int signum,const struct sigaction *act,struct sigaction *oldact));

#include


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);
}

其中,sa_restorer,已过时,POSIX不支持它,不应再被使用。

a,联合数据结构中的两个元素_sa_handler以及*_sa_sigaction指定信号关联函数,即用户指定的信号处理函数。除了可以是用户自

定义的处理函数外,还可以为SIG_DFL(采用缺省的处理方式),也可以为SIG_IGN(忽略信号)。

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

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

第一个参数为信号值,第三个参数没有使用(POSIX没有规范使用该参数的标准),第二个参数是指向siginfo_t结构的指针,结构中包含

信号携带的数据值,参数所指向的结构如下:


siginfo_t {
int si_signo; /* 信号值,对所有信号有意义*/
int si_errno; /* errno值,对所有信号有意义*/
int si_code; /* 信号产生的原因,对所有信号有意义*/
union{ /* 联合数据结构,不同成员适应不同信号 */
//确保分配足够大的存储空间
int _pad[SI_PAD_SIZE];
//对SIGKILL有意义的结构
struct{
...
}...

... ...
... ...
//对SIGILL, SIGFPE, SIGSEGV, SIGBUS有意义的结构
struct{
...
}...
... ...
}
}




注:为了更便于阅读,在说明问题时常把该结构表示为如下所表示的形式。

siginfo_t {
int si_signo; /* 信号值,对所有信号有意义*/
int si_errno; /* errno值,对所有信号有意义*/
int si_code; /* 信号产生的原因,对所有信号有意义*/
pid_t si_pid; /* 发送信号的进程ID,对kill(2),实时信号以及SIGCHLD有意义 */
uid_t si_uid; /* 发送信号进程的真实用户ID,对kill(2),实时信号以及SIGCHLD有意义 */
int si_status; /* 退出状态,对SIGCHLD有意义*/
clock_t si_utime; /* 用户消耗的时间,对SIGCHLD有意义 */
clock_t si_stime; /* 内核消耗的时间,对SIGCHLD有意义 */
sigval_t si_value; /* 信号值,对所有实时有意义,是一个联合数据结构,可以为一个整数(由si_int标示,也可以为一个指针,由si_ptr标示)*/

void * si_addr; /* 触发fault的内存地址,对SIGILL,SIGFPE,SIGSEGV,SIGBUS 信号有意义*/
int si_band; /* 对SIGPOLL信号有意义 */
int si_fd; /* 对SIGPOLL信号有意义 */

}


实际上,除了前三个元素外,其他元素组织在一个联合结构中,在联合数据结构中,又根据不同的信号组织成不同的结构。

注释中提到的对某种信号有意义指的是,在该信号的处理函数中可以访问这些域来获得与信号相关的有意义的信息,只不过特定信号只对特定信息感兴趣而已。
阅读(3269) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~