一.信号概念
每个信号都以SIG开头。
二.各信号的含义
SIGNAL
调用abort函数时产生的信号。进程异常终止。
SIGALRM 在用alarm函数设置的计时器超时时,产生此信号
SIGBUS 指示一个实现定义的硬件故障
SIGCHLD
在一个进程终止或停止时,将SIGCHLD信号发送给其父进程。一般用wait函数获得子进程ID和终止状态
SIGFPE 此信号表示一个算术运算异常。例如除以0
SIGHUP
如果终端接口检测到一个连接断开,则将此信号发送给与终端相关得控制进程。
SIGILL 此信号指示进程已执行一条非法硬件指令。
SIGINT
当用户按中断键(DELETE或Ctrl+C)时,终端驱动程序产生此信号并送至前台进程组中得每一个进程。
SIGIO 此信号指示一个异步I/O事件。
SIGTERM 这是由kill(1)命令发送得系统默认终止信号
SIGTSTP
交互式停止信号,当用户在终端上按下挂起键(一般为Ctrl+Z)时,终端驱动程序产生此信号。
SIGQUIT 当用户按下退出键(Ctrl+\)时,产生此信号,并送至前台进程组中得所有进程。
SIGUSR1
这是一个用户定义的信号,可用于应用程序
SIGUSR2
另一个用户定义的信号,与SIGUSR1相似
三.signal函数
UNIX系统得信号机制最简单得接口是signal函数
#include<signal.h>
void
(
*
signal
(
int
signo
,
void
(
*
func
)(
int
)))(
int
);
//返回值:若成功返回信号以前处理配置,出错返回SIG_ERR
signo参数为信号名,func的值是常量SIG_IGN、常量SIG_DFL或当接到此信号后要调用的函数的地址。当指定函数地址时,则在信号发生时,调用该函数,我们称这种处理为"捕捉"该信号,称此函数为
信号处理函数或信号捕捉函数。
signal函数需要两个参数,第一个是参数signo是一个整数,第二个参数是函数指针,它指向的函数需要一个整型参数。
例程:
#include "apue.h"
static
void
sig_user
(
int
signo
)
{
if
(
signo
==
SIGUSR1
)
printf
(
"receive SIGUSR1\n"
);
else
if
(
signo
==
SIGUSR2
)
printf
(
"receive SIGUSR2\n"
);
else
err_dump
(
"receive signal %d\n"
,
signo
);
}
int
main
()
{
if
(
signal
(
SIGUSR1
,
sig_user
)
==
SIG_ERR
)
err_sys
(
"can't catch SIGUSR1"
);
if
(
signal
(
SIGUSR2
,
sig_user
)
==
SIG_ERR
)
err_sys
(
"can't catch SIGUSR2"
);
for
(;;)
pause
();
//它使调用进程挂起直至捕捉到一个信号
}
运行结果:
kill -USR1 20768 //向该进程发送SIGUSR1
kill -USR2 20768 //向该进程发送SIGUSR2
在后台调用该程序,用kill(1)命令将信号传送给它,在UNIX中,杀死(kill)这个术语是不恰当的,kill(1)和kill(2)只是将一个信号送给一个进程或进程组。信号是否终止进程却决于信号的类型,以及进程是否安排了捕捉该信号。
四.不可靠信号
信号可能会丢失:一个信号发生了,但进程可能却一直不知道。
五.kill函数和raise函数
kill函数将信号发送给进程或进程组。raise函数则允许进程向
自身
发送信号。
函数定义:
#include<signal.h>
int
kill
(
pid_t
pid
,
int
signo
);
int
raise
(
int
signo
);
//两个函数的返回值:成功返回0,出错返回-1
其中:raise(signo);与kill(getpid(),signo);等价,即当前进程
kill的pid参数的4种情况:
pid>0 将该信号发送给进程ID为pid的进程
pid==0 将该信号发送给与发送进程属于同一进程组的所有进程。
pid<0 将该信号发送给其进程组ID等于pid的绝对值。
pid==-1 将该信号发送给发送进程有权限向他们发送信号的系统上的所有进程。
六.alarm和pause函数
使用
alarm函数
可以设置一个计时器,在某个指定的时间该计时器会超时,此时产生
SIGALRM
信号。如果不忽略或不捕捉此信号,其默认动作是
终止调用
该alarm函数的进程。
#include<unistd.h>
unsigned
int
alarm
(
unsigned
int
seconds
);
//返回值:0或以前设置的闹钟时间的余留秒数
参数seconds的值是秒数,经过seconds秒产生SIGALRM信号。经过指定秒数后,信号由内核产生,由于进程调度的延迟,进程得到控制从而处理该信号还需一些时间。
pause函数
将调用进程挂起直至捕捉到一个信号。
#include<unistd.h>
int
pause
(
void
);
//返回值:-1;并将errno设置为EINTR
只有执行了一个信号处理程序并从其中返回时,pause才返回。
例程:
#include "apue.h"
#include<setjmp.h>
#include<signal.h>
#include<unistd.h>
static
jmp_buf
env_alrm
;
static
void
sig_alrm
(
int
signo
)
{
longjmp
(
env_alrm
,
1
)
;
//调用后setjmp返回值为1,未调用时setjmp返回值为0
}
unsigned
int
sleep2
(
unsigned
int
nes
)
{
if
(
signal
(
SIGALRM
,
sig_alrm
)
==
SIG_ERR
)
//SIGALRM为计时器超时信号
return
(
nes
);
if
(
setjmp
(
env_alrm
)
==
0
)
//在没有计时器超时情况下,先执行此段语句,超时后执行signal,使setjmp返回值为1
{
alarm
(
nes
);
pause
();
}
return
(
alarm
(
0
));
}
static
void
sig_int
(
int
signo
)
{
int
i
,
j
;
volatile
int
k
;
//volatile阻止优化编译器丢弃循环语句
printf
(
"sig_int starting\n"
);
for
(
i
=
0
;
i
<
4
;
i
++
)
//使运行时间超过5s
for
(
j
=
0
;
j
<
4
;
j
++
)
k
+=
i
*
j
;
printf
(
"sig_int finished\n"
);
}
int
main
()
{
unsigned
int
unsleep
;
if
(
signal
(
SIGINT
,
sig_int
)
==
SIG_ERR
)
//SIGINT为中断信号
err_sys
(
"errno"
);
unsleep
=
sleep2
(
5
);
printf
(
"sleep2 returned:%u\n"
,
unsleep
);
exit
(
0
);
}
运行结果:
前一条运行语句是加了中断得结果,后一条为未加中断得结果
说明:
SIGINT处理程序中包含了for循环,执行时间超过5s,则运行结果中没有打印sig_int finished语句
七.信号集
信号集(sigset_t):一个能表示多个信号的数据类型
#include<signal.h>
int
sigemptyset
(
sigset_t
*
set
);
//初始化由set指向得信号集,清除其中的所有信号
int
sigfillset
(
sigset_t
*
set
);
//初始化由set指向的信号集,使其包括所有信号
int
sigaddset
(
sigset_t
*
set
,
int
signo
);
//增加一个信号到信号集
int
sigdelset
(
sigset_t
*
set
,
int
signo
);
//从信号集中删除一个信号
//四个函数得返回值:成功返回0,出错返回-1
int
sigismenber
(
const
sigset_t
*
set
,
int
signo
);
//返回值:若真返回1,若假返回0,出错返回-1
7.1 sigprocmask函数
告诉内核不允许发生该信号集中的信号。
信号屏蔽字:
一个进程的信号屏蔽字规定了当前阻塞而不能递送给该进程的信号集。调用sigprocmask可以检测或更改七信号屏蔽字,或者在一个步骤中同时执行者两个操作。
#include<signal.h>
int
sigprocmask
(
int
how
,
const
sigset_t
*
restrict
set
,
sigset_t
*
restrict
oset
);
//返回值:成功返回0,出错返回-1
(1)当oset是非空指针,进程当前信号屏蔽字通过oset返回
(2)若set是非空指针,参数how指示如何修改当前信号屏蔽字。
how的值:
SIG_BLOCK 该进程的新的信号屏蔽字是其当前信号屏蔽字和set指向信号集的并集。(或操作)set包含我们希望阻塞的
附加
信号
SIG_UNBLOCK 该进程的新的信号屏蔽字是其当前信号屏蔽字和set指向信号集的交集。(与操作)set包含我们希望
解除
阻塞的信号
SIG_SETMASK 改进程新的信号屏蔽字将被set指向的信号集的值
代替
7.2 sigpending函数
sigpending函数返回信号集,其中的各个信号对于调用进程是阻塞的而不能递送,因而也一定是当前未决的。该信号集通过set参数返回
#include<signal.h>
int
sigpending
(
sigset_t
*
set
);
//返回值:成功返回0,出错返回-1
7.3 sigaction函数
检查或修改与指定信号相关联的处理动作。
#include<signal.h>
int
sigaction
(
int
signo
,
const
struct
sigaction
*
restrict
act
,
struct
sigaction
*
restrict
oact
);
//返回值:成功返回0,出错返回-1
八.sigsetjmp和siglongjmp函数
在信号处理程序中进行非局部转移时应当使用这两个函数。
#include<setjmp.h>
int
sigsetjmp
(
sigjmp_buf
env
,
int
savemask
);
//返回值:直接调用返回0,从siglongjmp调用返回val值
int
siglongjmp
(
sigjmp_buf
env
,
int
val
);
若savemask为非0,则sigsetjmp再env中保存进程的当前信号屏蔽字,调用siglongjmp时,若带非0savemask的sigsetjmp调用已经保存了env,则siglongjmp从其中恢复保存的信号屏蔽字。
例程:
#include"apue.h"
#include<setjmp.h>
#include<time.h>
#include<errno.h>
static
void
sig_usr1
(
int
),
sig_alrm
(
int
);
void
pr_mask
(
const
char
*
);
static
sigjmp_buf
jmpbuf
;
static
volatile
sig_atomic_t
canjmp
;
int
main
()
{
if
(
signal
(
SIGUSR1
,
sig_usr1
)
==
SIG_ERR
)
err_sys
(
"signal SIGUSR1 ERR"
);
if
(
signal
(
SIGALRM
,
sig_alrm
)
==
SIG_ERR
)
err_sys
(
"signal SIGALRM ERR"
);
pr_mask
(
"starting main:"
);
if
(
sigsetjmp
(
jmpbuf
,
1
))
//未调用siglongjmp时,sigsetjmp返回值为0
{
pr_mask
(
"ending main: "
);
exit
(
0
);
}
canjmp
=
1
;
while
(
1
)
pause
();
//抓取信号
}
static
void
sig_usr1
(
int
signo
)
{
time_t
starttime
;
if
(
canjmp
==
0
)
return
;
pr_mask
(
"start sig_usr1:"
);
alarm
(
3
);
//3s后计时器超时
starttime
=
time
(
NULL
);
for
(;;)
//busy for 5s
if
(
time
(
NULL
)
>
starttime
+
5
)
break
;
pr_mask
(
"finishing sig_usr1: "
);
canjmp
=
0
;
siglongjmp
(
jmpbuf
,
1
);
//jump to main
}
static
void
sig_alrm
(
int
signo
)
{
pr_mask
(
"in sig_alrm: "
);
}
void
pr_mask
(
const
char
*
str
)
{
int
errno_save
;
sigset_t
sigset
;
errno_save
=
errno
;
if
(
sigprocmask
(
0
,
NULL
,
&
sigset
)
<
0
)
err_sys
(
"sigprocmask err"
);
printf
(
"%s"
,
str
);
if
(
sigismember
(
&
sigset
,
SIGUSR1
))
printf
(
"SIGUSR1 "
);
if
(
sigismember
(
&
sigset
,
SIGALRM
))
printf
(
"SIGALRM "
);
printf
(
"\n"
);
errno
=
errno_save
;
}
运行结果:
九.abort函数
异常程序终止
#include<stdlib.h>
void
abort
(
void
);
此函数将SIGABRT信号发送给调用进程。意图:在进程终止之前由其执行所需的清理操作,如果进程并不在信号处理程序中终止自己,abort终止该进程。