📄 linux环境进程间通信(二) 信号(上).htm
字号:
setitimer(int which, const struct itimerval *value, struct itimerval
*ovalue)); <BR>setitimer()比alarm功能强大,支持3种类型的定时器: </P>
<UL>
<LI>ITIMER_REAL: 设定绝对时间;经过指定的时间后,内核将发送SIGALRM信号给本进程;
<LI>ITIMER_VIRTUAL 设定程序执行时间;经过指定的时间后,内核将发送SIGVTALRM信号给本进程;
<LI>ITIMER_PROF
设定进程执行以及内核因本进程而消耗的时间和,经过指定的时间后,内核将发送ITIMER_VIRTUAL信号给本进程; </LI></UL>
<P>Setitimer()第一个参数which指定定时器类型(上面三种之一);第二个参数是结构itimerval的一个实例,结构itimerval形式见附录1。第三个参数可不做处理。</P>
<P>Setitimer()调用成功返回0,否则返回-1。</P>
<P>6、abort() <BR>#include <stdlib.h> <BR>void abort(void);
</P>
<P>向进程发送SIGABORT信号,默认情况下进程会异常退出,当然可定义自己的信号处理函数。即使SIGABORT被进程设置为阻塞信号,调用abort()后,SIGABORT仍然能被进程接收。该函数无返回值。</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt=""
src="Linux环境进程间通信(二) 信号(上).files/blue_rule.gif"
width="100%"><BR><IMG height=6 alt=""
src="Linux环境进程间通信(二) 信号(上).files/c.gif" width=8
border=0></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD><IMG height=4 alt=""
src="Linux环境进程间通信(二) 信号(上).files/c.gif" width="100%"><BR>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt=""
src="Linux环境进程间通信(二) 信号(上).files/u_bold.gif" width=16
border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox
href="http://www.ibm.com/developerworks/cn/linux/l-ipc/part2/index1.html#main"><B>回页首</B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=5><SPAN class=atitle>五、信号的安装(设置信号关联动作)</SPAN></A></P>
<P>如果进程要处理某一信号,那么就要在进程中安装该信号。安装信号主要用来确定信号值及进程针对该信号值的动作之间的映射关系,即进程将要处理哪个信号;该信号被传递给进程时,将执行何种操作。</P>
<P>linux主要有两个函数实现信号的安装:signal()、sigaction()。其中signal()在可靠信号系统调用的基础上实现,
是库函数。它只有两个参数,不支持信号传递信息,主要是用于前32种非实时信号的安装;而sigaction()是较新的函数(由两个系统调用实现:sys_signal以及sys_rt_sigaction),有三个参数,支持信号传递信息,主要用来与
sigqueue()
系统调用配合使用,当然,sigaction()同样支持非实时信号的安装。sigaction()优于signal()主要体现在支持信号带有参数。</P>
<P>1、signal() <BR>#include <signal.h> <BR>void (*signal(int
signum, void (*handler))(int)))(int);
<BR>如果该函数原型不容易理解的话,可以参考下面的分解方式来理解: <BR>typedef void
(*sighandler_t)(int); <BR>sighandler_t signal(int signum,
sighandler_t handler));
<BR>第一个参数指定信号的值,第二个参数指定针对前面信号值的处理,可以忽略该信号(参数设为SIG_IGN);可以采用系统默认方式处理信号(参数设为SIG_DFL);也可以自己实现处理方式(参数指定一个函数地址)。
<BR>如果signal()调用成功,返回最后一次为安装信号signum而调用signal()时的handler值;失败则返回SIG_ERR。
</P>
<P>2、sigaction() <BR>#include <signal.h> <BR>int sigaction(int
signum,const struct sigaction *act,struct sigaction *oldact)); </P>
<P>sigaction函数用于改变进程接收到特定信号后的行为。该函数的第一个参数为信号的值,可以为除SIGKILL及SIGSTOP外的任何一个特定有效的信号(为这两个信号定义自己的处理函数,将导致信号安装错误)。第二个参数是指向结构sigaction的一个实例的指针,在结构sigaction的实例中,指定了对特定信号的处理,可以为空,进程会以缺省方式对信号处理;第三个参数oldact指向的对象用来保存原来对相应信号的处理,可指定oldact为NULL。如果把第二、第三个参数都设为NULL,那么该函数可用于检查信号的有效性。</P>
<P>第二个参数最为重要,其中包含了对指定信号的处理、信号所传递的信息、信号处理函数执行过程中应屏蔽掉哪些函数等等。</P>
<P>sigaction结构定义如下:</P>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD class=code-outline><PRE class=displaycode> 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);
}
</PRE></TD></TR></TBODY></TABLE><BR>
<P>其中,sa_restorer,已过时,POSIX不支持它,不应再被使用。</P>
<P>1、联合数据结构中的两个元素_sa_handler以及*_sa_sigaction指定信号关联函数,即用户指定的信号处理函数。除了可以是用户自定义的处理函数外,还可以为SIG_DFL(采用缺省的处理方式),也可以为SIG_IGN(忽略信号)。</P>
<P>2、由_sa_handler指定的处理函数只有一个参数,即信号值,所以信号不能传递除信号值之外的任何信息;由_sa_sigaction是指定的信号处理函数带有三个参数,是为实时信号而设的(当然同样支持非实时信号),它指定一个3参数信号处理函数。第一个参数为信号值,第三个参数没有使用(posix没有规范使用该参数的标准),第二个参数是指向siginfo_t结构的指针,结构中包含信号携带的数据值,参数所指向的结构如下:</P>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD class=code-outline><PRE class=displaycode>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{
...
}...
... ...
}
}
</PRE></TD></TR></TBODY></TABLE><BR>
<P>注:为了更便于阅读,在说明问题时常把该结构表示为附录2所表示的形式。</P>
<P>siginfo_t结构中的联合数据成员确保该结构适应所有的信号,比如对于实时信号来说,则实际采用下面的结构形式:</P>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD class=code-outline><PRE class=displaycode> typedef struct {
int si_signo;
int si_errno;
int si_code;
union sigval si_value;
} siginfo_t;
</PRE></TD></TR></TBODY></TABLE><BR>
<P>结构的第四个域同样为一个联合数据结构:</P>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD class=code-outline><PRE class=displaycode> union sigval {
int sival_int;
void *sival_ptr;
}
</PRE></TD></TR></TBODY></TABLE><BR>
<P>采用联合数据结构,说明siginfo_t结构中的si_value要么持有一个4字节的整数值,要么持有一个指针,这就构成了与信号相关的数据。在信号的处理函数中,包含这样的信号相关数据指针,但没有规定具体如何对这些数据进行操作,操作方法应该由程序开发人员根据具体任务事先约定。</P>
<P>前面在讨论系统调用sigqueue发送信号时,sigqueue的第三个参数就是sigval联合数据结构,当调用sigqueue时,该数据结构中的数据就将拷贝到信号处理函数的第二个参数中。这样,在发送信号同时,就可以让信号传递一些附加信息。信号可以传递信息对程序开发是非常有意义的。</P>
<P>信号参数的传递过程可图示如下:</P><BR><IMG height=178 alt=""
src="Linux环境进程间通信(二) 信号(上).files/2.gif" width=586 border=0> <BR>
<P>3、sa_mask指定在信号处理程序执行过程中,哪些信号应当被阻塞。缺省情况下当前信号本身被阻塞,防止信号的嵌套发送,除非指定SA_NODEFER或者SA_NOMASK标志位。</P>
<P>注:请注意sa_mask指定的信号阻塞的前提条件,是在由sigaction()安装信号的处理函数执行过程中由sa_mask指定的信号才被阻塞。</P>
<P>4、sa_flags中包含了许多标志位,包括刚刚提到的SA_NODEFER及SA_NOMASK标志位。另一个比较重要的标志位是SA_SIGINFO,当设定了该标志位时,表示信号附带的参数可以被传递到信号处理函数中,因此,应该为sigaction结构中的sa_sigaction指定处理函数,而不应该为sa_handler指定信号处理函数,否则,设置该标志变得毫无意义。即使为sa_sigaction指定了信号处理函数,如果不设置SA_SIGINFO,信号处理函数同样不能得到信号传递过来的数据,在信号处理函数中对这些信息的访问都将导致段错误(Segmentation
fault)。</P>
<P>注:很多文献在阐述该标志位时都认为,如果设置了该标志位,就必须定义三参数信号处理函数。实际不是这样的,验证方法很简单:自己实现一个单一参数信号处理函数,并在程序中设置该标志位,可以察看程序的运行结果。实际上,可以把该标志位看成信号是否传递参数的开关,如果设置该位,则传递参数;否则,不传递参数。</P><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=1 alt=""
src="Linux环境进程间通信(二) 信号(上).files/blue_rule.gif"
width="100%"><BR><IMG height=6 alt=""
src="Linux环境进程间通信(二) 信号(上).files/c.gif" width=8
border=0></TD></TR></TBODY></TABLE>
<TABLE class=no-print cellSpacing=0 cellPadding=0 align=right>
<TBODY>
<TR align=right>
<TD><IMG height=4 alt=""
src="Linux环境进程间通信(二) 信号(上).files/c.gif" width="100%"><BR>
<TABLE cellSpacing=0 cellPadding=0 border=0>
<TBODY>
<TR>
<TD vAlign=center><IMG height=16 alt=""
src="Linux环境进程间通信(二) 信号(上).files/u_bold.gif" width=16
border=0><BR></TD>
<TD vAlign=top align=right><A class=fbox
href="http://www.ibm.com/developerworks/cn/linux/l-ipc/part2/index1.html#main"><B>回页首</B></A></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR><BR>
<P><A name=6><SPAN class=atitle>六、信号集及信号集操作函数:</SPAN></A></P>
<P>信号集被定义为一种数据类型:</P>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD class=code-outline><PRE class=displaycode> typedef struct {
unsigned long sig[_NSIG_WORDS];
} sigset_t
</PRE></TD></TR></TBODY></TABLE><BR>
<P>信号集用来描述信号的集合,linux所支持的所有信号可以全部或部分的出现在信号集中,主要与信号阻塞相关函数配合使用。下面是为信号集操作定义的相关函数:</P>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD class=code-outline><PRE class=displaycode> #include <signal.h>
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set, int signum)
int sigdelset(sigset_t *set, int signum);
int sigismember(const sigset_t *set, int signum);
sigemptyset(sigset_t *set)初始化由set指定的信号集,信号集里面的所有信号被清空;
sigfillset(sigset_t *set)调用该函数后,set指向的信号集中将包含linux支持的64种信号;
sigaddset(sigset_t *set, int signum)在set指向的信号集中加入signum信号;
sigdelset(sigset_t *set, int signum)在set指向的信号集中删除signum信号;
sigismember(const sigset_t *set, int signum)判定信号signum是否在set指向的信号集中。
</PRE></TD></TR></TBODY></TABLE><BR><BR>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -