您的位置:首页 > 编程语言

UNIX信号(signal)编程 - UNIX高级环境编程第10章读书笔记

2007-12-14 19:05 417 查看

其他章节

使用pthread库进行多线程编程1 - UNIX环境高级编程第11章读书笔记
使用pthread库进行多线程编程2 - UNIX高级环境编程第12章读书笔记10 Signals

1 Introduction & Concepts

Signals是一种软件中断,通知程序某种事件的发生。常见的Signal有SIGABRT(当进程调用abort函数的时候自动发送), SIGALRM(当timer被触发的时候自动发送),等等。下面的情况可以产生Signal:1.     按下CTRL+C产生SIGINT2.     硬件中断,如除0,非法内存访问(SIGSEV)等等3.     Kill函数可以对进程发送Signal4.     Kill命令。实际上是对Kill函数的一个包装5.     软件中断。如当Alarm Clock超时(SIGURG),当Reader中止之后又向管道写数据(SIGPIPE),等等当Signal发生的时候,可以有三种选择(称之为Disposition of the signal或者Action associated with a signal)1.     忽略Signal。大部分Signal都可以Ignore,除了SIGKILL和SIGSTOP,这是为了提供一个确定的方法来Stop或者Kill一个Process。此外,如果我们忽略部分硬件异常产生的Signal,进程的行为未定义。2.     捕捉Signal。可以让内核来调用我们所指定的函数。SIGKILL和SIGSTOP无法捕捉。3.     执行缺省行为。如果不做任何处理,则执行缺省动作。大部分Signal的缺省行为都是中止进程。部分Signal的缺省行为不仅中止进程,同时还会产生core dump,也就是生成一个名为core的文件,其中保存了退出时进程内存的镜像,可以用来调试。在下面情况,不会生成core文件:1.     当前进程不属于当前user2.     当前进程不属于当前group3.     用户在当前目录下无写权限4.     Core文件已存在,用户无写权限5.     文件过大,超过RLIMIT_CORE

2 Signals

 
Signal Description
SIGABRT 由调用abort函数产生,进程非正常退出
SIGALRM 用alarm函数设置的timer超时或setitimer函数设置的interval timer超时
SIGBUS 某种特定的硬件异常,通常由内存访问引起
SIGCANCEL 由Solaris Thread Library内部使用,通常不会使用
SIGCHLD 进程Terminate或Stop的时候,SIGCHLD会发送给它的父进程。缺省情况下该Signal会被忽略
SIGCONT 当被stop的进程恢复运行的时候,自动发送
SIGEMT 和实现相关的硬件异常
SIGFPE 数学相关的异常,如被0除,浮点溢出,等等
SIGFREEZE Solaris专用,Hiberate或者Suspended时候发送
SIGHUP 发送给具有Terminal的Controlling Process,当terminal被disconnect时候发送
SIGILL 非法指令异常
SIGINFO BSD signal。由Status Key产生,通常是CTRL+T。发送给所有Foreground Group的进程
SIGINT 由Interrupt Key产生,通常是CTRL+C或者DELETE。发送给所有ForeGround Group的进程
SIGIO 异步IO事件
SIGIOT 实现相关的硬件异常,一般对应SIGABRT
SIGKILL 无法处理和忽略。中止某个进程
SIGLWP 由Solaris Thread Libray内部使用
SIGPIPE 在reader中止之后写Pipe的时候发送
SIGPOLL 当某个事件发送给Pollable Device的时候发送
SIGPROF Setitimer指定的Profiling Interval Timer所产生
SIGPWR 和系统相关。和UPS相关。
SIGQUIT 输入Quit Key的时候(CTRL+/)发送给所有Foreground Group的进程
SIGSEGV 非法内存访问
SIGSTKFLT Linux专用,数学协处理器的栈异常
SIGSTOP 中止进程。无法处理和忽略。
SIGSYS 非法系统调用
SIGTERM 请求中止进程,kill命令缺省发送
SIGTHAW Solaris专用,从Suspend恢复时候发送
SIGTRAP 实现相关的硬件异常。一般是调试异常
SIGTSTP Suspend Key,一般是Ctrl+Z。发送给所有Foreground Group的进程
SIGTTIN 当Background Group的进程尝试读取Terminal的时候发送
SIGTTOU 当Background Group的进程尝试写Terminal的时候发送
SIGURG 当out-of-band data接收的时候可能发送
SIGUSR1 用户自定义signal 1
SIGUSR2 用户自定义signal 2
SIGVTALRM setitimer函数设置的Virtual Interval Timer超时的时候
SIGWAITING Solaris Thread Library内部实现专用
SIGWINCH 当Terminal的窗口大小改变的时候,发送给Foreground Group的所有进程
SIGXCPU 当CPU时间限制超时的时候
SIGXFSZ 进程超过文件大小限制
SIGXRES Solaris专用,进程超过资源限制的时候发送
 

3 Signal Function

 
#include   Void (*signal(int signo, void (*func)(int)))(int);   返回之前的signal处理函数,错误返回SIG_ERR
 1.     Signo:signal的名称2.     Func:函数地址,或者是SIG_IGN(忽略Signal)或SIG_DFL(缺省行为)。原型为:void (*)(int)。不过很多UNIX的实现也会传入一些和实现相关的参数如果用exec创建子进程,那么子进程的所有Signal状态是缺省或者忽略:1.     如果父进程忽略了某个Signal,那么父进程用exec创建子进程时子进程的这个Signal的状态也是忽略2.     如果在父进程的某个Signal注册了Signal处理函数的话,那么这个子进程的该Signal的状态会恢复成缺省状态,因为注册的函数地址肯定在另外一个进程中无法调用。如果用fork创建子进程,那么子进程会继承所有父进程的Signal状态。

4 Unreliable Signals

早期的UNIX系统的Signal是不稳定的:1.     Signal可能会Lost2.     在signal处理函数中,需要重复注册signal,否则下次收不到signal3.     不支持pending signal

5 Interrupted System Calls

1.     早期UNIX中,一个进程在调用系统调用被阻赛的时候, 有可能被一个Signal中断。此时系统调用返回错误并且errno被设置为EINTR。2.     系统调用也被分为两类:slow和一般。slow系统调用是那些可能永远block的系统调用(disk IO是例外,一般情况下只会暂时Block,但也归入Slow一类)3.     为了处理这个问题,在早期UNIX系统中对于这些系统调用需要作额外的检查和判断,如果错误并且errno=EINTR,需要重新调用4.     Free BSD  4.2对于部分系统调用ioctl, read, readv, write, writev, wait实现了自动重新恢复功能,也就是当函数被中断的时候会自动恢复到原来的执行情况5.     Free BSD 5.2.1, Linux 2.4.22, Mac OS X 10.3支持此功能,但POSIX.1不作强制要求

6 Reentrant Functions

在Signal处理函数中,有可能在执行某个函数的时候,signal产生并调用了signal处理函数,然后再signal处理函数中再次调用了此函数,如果此函数调用了malloc/free,或者使用了static变量等技巧,会造成此函数不可再次被调用,此函数就是不可重入,反之就是可以重入的。UNIX标准定义了一个表,表中的函数都是可重入的。

7 Reliable Signal Terminology and Semantics

1.     在一个Signal被Generate之后,而被Deliver之前,该Signal称为正处于pending状态2.     进程可以Block某个Signal。如果某个被Block的signal产生的话,该signal一直会处于pending状态,直到进程a.     Unblock这个Signalb.     改变Signal的状态为Ignore3.     如果一个被Block的Signal产生多次,POSIX.1允许deliver该signal一次或者多次4.     每个进程有一个signal mask,表示那些signal被Block,那些被允许5.     sigset_t用来表示多种signal的集合

8 kill and raise Functions

Kill函数对某个进程发送signal,而raise对本进程发送signal:
#include   int kill(pit_t pid, int signo); int raise(int signo)   成功返回0,错误返回-1
 对于pid可以有4种可能性:1.     pid > 0:发送给某个进程2.     pid == 0:发送给所有group ID=当前发送进程的group id,并且有权限发送。注意部分系统进程(具体是那些的话和具体实现相关)不包括在内3.     pid < 0:发送给所有group ID= abs(pid)的进程,不包括部分系统进程4.     pid == -1:发送给所有有权限发送signal的进程,不包括部分系统进程顺便说一句,我认为这个design不太好,最好设计成四个不同函数。Superuser可以发送signal给任何进程,而对于一般用户来说,发送者的user id必须等于接收者的user id如果signo=0,则是告诉kill函数检查该进程是否存在。如果不存在,则返回错误-1,errno设置为ESRCH。

9 alarm and pause Functions

Alarm函数设置一个timer,到固定时间之后会expire,发送一个SIGALRM signal:
#include   unsigned int alarm(unsigned int second);   返回前一个alarm的剩余时间值,单位为秒
 SIGALRM的缺省行为是中止进程。同一个进程只能有一个alarm。调用alarm函数会取消前一个alarm,并返回前一个alarm的剩余时间。如果second值为0,则只是取消前一个alarm。 Pause函数暂停当前进程的运行,直到某个signal被catch:
#include   int pause(void);   总是返回-1,errno总是设置为EINTR
 

10 signal sets

多个signal的集合用signal set表示,类型为sigset_t,下面函数用于操作sigset_t:
#include   int sigemptyset(sigset_t *set);   int sigfillset(sigset_t *set);   int sigaddset(sigset_t *set, int signo);   int sigdelset(sigset_t *set, int signo);   成功返回0,错误返回-1   int sigismember(sigset_t *set, int signo);   返回1如果是,不是则返回0
 1.     sigemptyset:初始化sigset_t,所有signal都不包括在内2.     sigfillset:初始化sigset_t,包括所有signal3.     sigaddset, sigdelset:添加/删除signal4.     sigismember:检查signal set中是否包括该signal

11 sigprocmask Function

sigprocmask函数设置进程的signal mask,可以block/unblock signal:
#include   int sigprocmask(int how, const sigset_t *restrict set, sig_set *restrict oset);     成功返回0,错误返回-1
1.     how参数的意义如下:
How Description
SIG_BLOCK 在原来的mask基础上Block signal set
SIG_UNBLOCK 在原来的mask基础上Unblock signal set
SIG_SETMASK 设置mask为指定的signal set
 2.     set:signal set,如果=NULL则忽略how参数(again,又是一个不太合适的设计,不利于差错和记忆)3.     oset:返回当前process的signal mask 

12 sigpending Function

Sigpending函数返回当前被block而没有deliver的signal的集合:
#include   int sigpending(sigset_t *set);     成功返回0,错误返回-1
 

13 sigaction Function

sigaction用于修改或者获取某个signal所对应的action。Sigaction可以取代Signal函数,实际上,signal函数可以用sigaction函数来实现。。sigaction函数原型如下:
#include   int sigaction(int signo, const struct sigaction *restrict act, struct sigaction *restrict oact);     成功返回0,错误返回-1
1.     signo:signal2.     act:对应的action3.     oact:之前的action,可以为NULLsigaction如下:
struct sigaction {       void        (*sa_handler)(int);       sigset_t    sa_mask;       int         sa_flags;       void        (*sa_sigaction)(int, siginfo_t *, void *) };
1.     sa_handler:signal处理函数2.     sa_mask:当signal函数被调用的时候,自动block这些signal。当signal处理函数返回的时候,mask会恢复到初始值。同时,操作系统也会自动把该signal加到mask中,防止重入3.     sa_flags:定义如下:
Option Description
SA_INTERRUPT 被该signal中断的系统调用不会自动重启
SA_NOCLDSTOP 如果signo是SIGCHLD,当子进程stop的时候不产生该signal
SA_NOCLDWAIT 如果signo是SIGCHLD,子进程不会变成zombie。当调用wait的时候,调用进程会一直等待直到所有子进程结束
SA_NODEFER 在signal处理函数中系统不会自动block该signal
SA_ONSTACK 如果调用了sigaltstack指定一个额外的stack,那么该signal发送的时候,进程处于此stack中
SA_RESETHAND 恢复为初始化值,SA_SIGINFO flag也会被清除
SA_RESTART 被该signal中断的系统调用会自动重启
SA_SIGINFO 为signal处理函数提供额外信息
 4.     sa_sigaction:当sa_flags为SA_SIGINFO时,signal处理函数为sa_sigaction,接收额外两个参数:siginfo_t和void *siginfo_t表示该signal为何产生:
struct siginfo_t {       int   si_signo;   /* signal */       int   si_errno;   /* errrno */       int   si_code;    /* 额外信息,和具体signal相关 */       pid_t si_pid;     /* 发送signal进程的pid */       uid_t si_uid;     /* 发送signal的进程的uid */       void  *si_addr;   /* 产生fault(SIG_SEGV)的地址 */       int   si_status;  /* 退出值或者signal数值 */       long  si_band;    /* SIGPOLL的band数值 */ };
 而void *则应该被转换成ucontext_t结构,表示当signal产生时候的进程上下文信息 

14 sigsetjmp and siglongjmp Functions

1.     setjmp,longjmp的函数的作用是,setjmp记住位置,然后longjmp可以自动跳转到该位置,并且从setjmp函数返回2.     当调用signal处理函数的时候,该signal会自动被block,如果这个时候我们调用longjmp跳出signal处理函数,那么signal还会被Block吗?答案是不确定,和具体实现相关3.     为了解决这个问题,系统提供了sigsetjmp和siglongjmp。区别在于,如果sigsetjmp的savemask为非0,则sigsetjmp会在env参数中记住当前process的signal mask。一般情况下如果和signal打交道的话都需要调用这两个函数。这两个函数原型如下:
#include   int sigsetjmp(sigjmp_buf env, int savemask);   直接调用返回0,从siglongjmp返回的时候返回非0   void siglongjmp(sigjmp_buf env, int val);
 

15 sigsuspend Function

Sigsuspend函数可以在一个原子操作内设置mask然后pause,防止设置mask之后pause之前,signal handler被调用,进程永远等待。原型如下:
#include   int sigsuspend(sigset_t *sig);   总是返回-1,errno设置为EINTR
 

16 abort Function

Abort函数等价于raise (SIGABRT)。注意就算是signal处理函数处理了SIGABRT,进程仍然会结束。

17 system Function

System函数除了会执行可执行文件创建子进程之外,还会设置sigmask为忽略SIGINT,SIGQUIT并Block SIGCHLD。1.     忽略SIGINT和SIGQUIT的原因是,只有子进程应该处理这两个signal,而父进程无需处理2.     Block SIGCHLD的原因是,System函数需要知道子进程的结束,而父进程不应该先提前知道,以免提前调用wait函数使得System函数本身无法获得进程的退出值

18 sleep Function

Sleep函数可以sleep某段时间,精确性无法保证。进程睡眠指定时间,直到时间到或者signal产生并被handler处理。注意sleep可能会由alarm函数实现,所以把alarm函数和sleep函数混着使用可能会造成无法预料的结果。

19 Job-Control Signals

SIGCHLD, SIGCONT, SIGSTOP, SITTSTP, SIGTTIN, SITTOU被认为是和Job Control有关的signal。大部分情况下,程序无需处理这些signal,除了SIGCHLD之外。

20 Additional Features

有些系统提供sys_siglist数组,保存着每个signal对应的字符串描述。
extern char *sys_siglist[];
 很多系统提供psignal函数输出一个msg + ‘: ’ + signal string
#include   void psignal(int四个,const char *msg);
 另一个常用函数是strsignal,返回signo对应的描述
#include   char *strsignal(int signo);   返回对应的描述
 Solaris提供把signal和名称对应起来的函数:
#include   int *sig2str(int signo, char *str);   int str2sig(const char *str, int *signop);   返回对应的描述
Sig2str把signal转换为对应的描述字符串,而str2sig则是将描述字符串转换为signal。 

Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1532506
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: