Chinaunix首页 | 论坛 | 博客
  • 博客访问: 6270805
  • 博文数量: 2759
  • 博客积分: 1021
  • 博客等级: 中士
  • 技术积分: 4091
  • 用 户 组: 普通用户
  • 注册时间: 2012-03-11 14:14
文章分类

全部博文(2759)

文章存档

2019年(1)

2017年(84)

2016年(196)

2015年(204)

2014年(636)

2013年(1176)

2012年(463)

分类: LINUX

2013-09-11 12:13:48

原文地址:linux信号处理 作者:list_linux

1. 信号概念
信号是进程在运行过程中,由自身产生或由进程外部发过来的消息(事件)。信号是硬件中断的软件模拟(软中断)。每个信号用一个整型常量宏表示,以SIG开头,比如SIGCHLD、SIGINT等,它们在系统头文件中定义,也可以通过在shell下键入kill –l查看信号列表,或者键入man 7 signal查看更详细的说明。
信号的生成来自内核,让内核生成信号的请求来自3个地方:
l         用户:用户能够通过输入CTRL+c、Ctrl+\,或者是终端驱动程序分配给信号控制字符的其他任何键来请求内核产生信号;
l         内核:当进程执行出错时,内核会给进程发送一个信号,例如非法段存取(内存访问违规)、浮点数溢出等;
l         进程:一个进程可以通过系统调用kill给另一个进程发送信号,一个进程可以通过信号和另外一个进程进行通信。信号(signal)是一种进程间通信机制,
                    它给应用程序提供一种异步的软件中断,使应用程序有机会接受其他程序活终端发送的命令(即信号)。

由进程的某个操作产生的信号称为同步信号(synchronous signals),例如除0;由象用户击键这样的进程外部事件产生的信号叫做异步信号。(asynchronous signals)。
进程接收到信号以后,可以有如下3种选择进行处理:
l         接收默认处理:接收默认处理的进程通常会导致进程本身消亡。例如连接到终端的进程,用户按下CTRL+c,将导致内核向进程发送一个SIGINT的信号,进程如果不对该信号做特殊的处理,系统将采用默认的方式处理该信号,即终止进程的执行;
l         忽略信号:进程可以通过代码,显示地忽略某个信号的处理,例如:signal(SIGINT,SIGDEF);但是某些信号是不能被忽略的,
l         捕捉信号并处理:进程可以事先注册信号处理函数,当接收到信号时,由信号处理函数自动捕捉并且处理信号。

有两个信号既不能被忽略也不能被捕捉,它们是SIGKILL和SIGSTOP。即进程接收到这两个信号后,只能接受系统的默认处理,即终止线程。
2. signal信号处理机制
可以用函数signal注册一个信号捕捉函数。原型为:
#include
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);

signal的第1个参数signum表示要捕捉的信号,第2个参数是个函数指针,表示要对该信号进行捕捉的函数,该参数也可以是SIG_DEF(表示交由系统缺省处理,相当于白注册了)或SIG_IGN(表示忽略掉该信号而不做任何处理)。signal如果调用成功,返回以前该信号的处理函数的地址,否则返回SIG_ERR。
sighandler_t是信号捕捉函数,由signal函数注册,注册以后,在整个进程运行过程中均有效,并且对不同的信号可以注册同一个信号捕捉函数。该函数只有一个参数,表示信号值。
示例:
1、  捕捉终端CTRL+c产生的SIGINT信号:
#include
#include
#include
#include

void SignHandler(int iSignNo)
{
printf("Capture sign no:%d\n",iSignNo);
}

int main()
{
signal(SIGINT,SignHandler);
while(true)
sleep(1);
return 0;
}
该程序运行起来以后,通过按 CTRL+c将不再终止程序的运行。应为CTRL+c产生的SIGINT信号已经由进程中注册的SignHandler函数捕捉了。该程序可以通过 Ctrl+\终止,因为组合键Ctrl+\能够产生SIGQUIT信号,而该信号的捕捉函数尚未在程序中注册。
2、  忽略掉终端CTRL+c产生的SIGINT信号:
#include
#include
#include
#include

int main()
{
signal(SIGINT,SIG_IGN);
while(true)
sleep(1);
return 0;
}
该程序运行起来以后,将CTRL+C产生的SIGINT信号忽略掉了,所以CTRL+C将不再能是该进程终止,要终止该进程,可以向进程发送SIGQUIT信号,即组合键CTRL+\

3、  接受信号的默认处理,接受默认处理就相当于没有写信号处理程序:

#include
#include
#include
#include

int main()
{
signal(SIGINT,DEF);
while(true)
sleep(1);
return 0;
}
3. sigaction信号处理机制
3.1. 信号处理情况分析
在signal处理机制下,还有许多特殊情况需要考虑:
1、  册一个信号处理函数,并且处理完毕一个信号之后,是否需要重新注册,才能够捕捉下一个信号;
2、  如果信号处理函数正在处理信号,并且还没有处理完毕时,又发生了一个同类型的信号,这时该怎么处理;
3、  如果信号处理函数正在处理信号,并且还没有处理完毕时,又发生了一个不同类型的信号,这时该怎么处理;
4、  如果程序阻塞在一个系统调用(如read(...))时,发生了一个信号,这时是让系统调用返回错误再接着进入信号处理函数,还是先跳转到信号处理函数,等信号处理完毕后,系统调用再返回。

示例:
#include
#include
#include
#include

int g_iSeq=0;

void SignHandler(int iSignNo)
{
int iSeq=g_iSeq++;
printf("%d Enter SignHandler,signo:%d.\n",iSeq,iSignNo);
sleep(3);
printf("%d Leave SignHandler,signo:%d\n",iSeq,iSignNo);
}

int main()
{
char szBuf[8];
int iRet;
signal(SIGINT,SignHandler);
signal(SIGQUIT,SignHandler);
do{
iRet=read(STDIN_FILENO,szBuf,sizeof(szBuf)-1);
if(iRet<0){
perror("read fail.");
break;
}
szBuf[iRet]=0;
printf("Get: %s",szBuf);
}while(strcmp(szBuf,"quit\n")!=0);
return 0;
}
程序运行时,针对于如下几种输入情况(要输入得快),看输出结果:
1、  CTRL+c] [CTRL+c] [CTRL+c]
2、  [CTRL+c] [CTRL+\]
3、  hello [CTRL+\] [Enter]
4、  [CTRL+\] hello [Enter]
5、  hel [CTRL+\] lo[Enter]

针对于上面各种情况,不同版本OS可能有不同的响应结果。
3.2. sigaction信号处理注册
如果要想用程序控制上述各种情况的响应结果,就必须采用新的信号捕获机制,即使用sigaction信号处理机制。
函数原型:
#include
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
sigaction也用于注册一个信号处理函数。
参数signum为需要捕捉的信号;
参数 act是一个结构体,里面包含信号处理函数地址、处理方式等信息。
参数oldact是一个传出参数,sigaction函数调用成功后,oldact里面包含以前对signum的处理方式的信息。
如果函数调用成功,将返回0,否则返回-1
结构体 struct sigaction(注意名称与函数sigaction相同)的原型为:
struct sigaction {
void (*sa_handler)(int);         // 老类型的信号处理函数指针
void (*sa_sigaction)(int, siginfo_t *, void *);//新类型的信号处理函数指针
sigset_t sa_mask;                 // 将要被阻塞的信号集合
int sa_flags;                         // 信号处理方式掩码
void (*sa_restorer)(void);     // 保留,不要使用。
}
该结构体的各字段含义及使用方式:
1、字段sa_handler是一个函数指针,用于指向原型为void handler(int)的信号处理函数地址,       即老类型       的信号处理函数;
2、字段sa_sigaction也是一个函数指针,用于指向原型为:
void handler(int iSignNum,siginfo_t *pSignInfo,void *pReserved);
的信号处理函数,即新类型的信号处理函数。
该函数的三个参数含义为:
iSignNum :传入的信号
pSignInfo :与该信号相关的一些信息,它是个结构体
pReserved :保留,现没用
3、字段sa_handler和sa_sigaction只应该有一个生效,如果想采用老的信号处理机制,就应该让sa_handler指向正确的信号处理函数;否则应该让sa_sigaction指向正确的信号处理函数,并且让字段 sa_flags包含SA_SIGINFO选项。
4、字段sa_mask是一个包含信号集合的结构体,该结构体内的信号表示在进行信号处理时,将要被阻塞的信号。针对sigset_t结构体,有一组专门的函数对它进行处理,它们是:
#include
int sigemptyset(sigset_t *set);                                   // 清空信号集合set
int sigfillset(sigset_t *set);                                 // 将所有信号填充进set中
int sigaddset(sigset_t *set, int signum);               // 往set中添加信号signum
int sigdelset(sigset_t *set, int signum);                // 从set中移除信号signum
int sigismember(const sigset_t *set, int signum); // 判断signnum是不是包含在set中
例如,如果打算在处理信号SIGINT时,只阻塞对SIGQUIT信号的处理,可以用如下种方法:
struct sigaction act;
sigemptyset(&act.sa_mask);
sigaddset(&act_sa_mask,SIGQUIT);
sigaction(SIGINT,&act,NULL);
5、  字段sa_flags是一组掩码的合成值,指示信号处理时所应该采取的一些行为,各掩码的含义为:

掩码

描述
SA_RESETHAND

处理完毕要捕捉的信号后,将自动撤消信号处理函数的注册,即必须再重新注册信号处理函数,才能继续处理接下来产生的信号。该选项不符合一般的信号处理流程,现已经被废弃。
SA_NODEFER

在处理信号时,如果又发生了其它的信号,则立即进入其它信号的处理,等其它信号处理完毕后,再继续处理当前的信号,即递规地处理。如果 sa_flags包含了该掩码,则结构体sigaction的sa_mask将无效!
SA_RESTART

如果在发生信号时,程序正阻塞在某个系统调用,例如调用read()函数,则在处理完毕信号后,接着返回阻塞的系统调用。该掩码符合普通的程序处理流程,所以一般来说,应该设置该掩码,否则信号处理完后,阻塞的系统调用将会返回失败!
SA_SIGINFO

指示结构体的信号处理函数指针是哪个有效,如果sa_flags包含该掩码,则sa_sigactiion指针有效,否则是 sa_handler指针有效。


练习与验证:
针对于先前的5种输入情况,给下面代码再添加一些代码,使之能够进行如下各种形式的响应:
1 、[CTRL+c] [CTRL+c]时,第1个信号处理阻塞第2个信号处理;
2 、[CTRL+c] [CTRL+c]时,第1个信号处理时,允许递规地第2个信号处理;
3 、[CTRL+c] [CTRL+\]时,第1个信号阻塞第2个信号处理;
4 、read不要因为信号处理而返回失败结果。

#include
#include
#include
#include

int g_iSeq=0;

void SignHandlerNew(int iSignNo,siginfo_t *pInfo,void *pReserved)
{
int iSeq=g_iSeq++;
printf("%d Enter SignHandlerNew,signo:%d.\n",iSeq,iSignNo);
sleep(3);
printf("%d Leave SignHandlerNew,signo:%d\n",iSeq,iSignNo);
}

int main()
{
char szBuf[8];
int iRet;
struct sigaction act;
act.sa_sigaction=SignHandlerNew;
act.sa_flags=SA_SIGINFO;
//
sigemptyset(&act.sa_mask);
sigaction(SIGINT,&act,NULL);
sigaction(SIGQUIT,&act,NULL);
do{
iRet=read(STDIN_FILENO,szBuf,sizeof(szBuf)-1);
if(iRet<0){
perror("read fail.");
break;
}
szBuf[iRet]=0;
printf("Get: %s",szBuf);
}while(strcmp(szBuf,"quit\n")!=0);
return 0;
}

3.3 中断与自动重启动
前面说过,信号是一种软件中断机制,这就产生了一个问题:如果信号到来时进城正在执行某个低速系统调用,系统应该怎么处理?是暂时阻塞系统调用返回,在信号处理程序完成后继续没完成的系统调用呢,还是让系统调用出错返回,同时把errno设置为EINTR。
对由signal()函数安装的信号处理程序,系统默认会自动重启动被中断的系统调用,而不是让它出错返回,所以应用程序不必针对慢速系统调用的errno,做EINTR检查,这就是自动重启动机制。

对由sigaction()函数安装的信号处理程序,系统的默认动作是不自动重启动被中断的系统调用。因此我们在使用sigaction()时需要自动重启动被中断的系统调用,就需要使用sigaction的SA_RESTART选项。

3.4. sigprocmask信号阻塞
函数sigaction中设置的被阻塞信号集合只是针对于要处理的信号,例如
struct sigaction act;
sigemptyset(&act.sa_mask);
sigaddset(&act.sa_mask,SIGQUIT);
sigaction(SIGINT,&act,NULL);
表示只有在处理信号SIGINT时,才阻塞信号SIGQUIT;
函数sigprocmask是全程阻塞,在sigprocmask中设置了阻塞集合后,被阻塞的信号将不能再被信号处理函数捕捉,直到重新设置阻塞信号集合。
原型为:
#include
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
参数how的值为如下3者之一:
a :SIG_BLOCK ,将参数2的信号集合添加到进程原有的阻塞信号集合中
b :SIG_UNBLOCK ,从进程原有的阻塞信号集合移除参数2中包含的信号
c :SIG_SET,重新设置进程的阻塞信号集为参数2的信号集
参数set为阻塞信号集。如果set为NULL,则不修改当前信号屏蔽字,而将当前信号集通过oldset返回;
参数oldset是传出参数,存放进程原有的信号集。
示例:
#include
#include
#include
#include

int g_iSeq=0;

void SignHandlerNew(int iSignNo,siginfo_t *pInfo,void *pReserved)
{
int iSeq=g_iSeq++;
printf("%d Enter SignHandlerNew,signo:%d.\n",iSeq,iSignNo);
sleep(3);
printf("%d Leave SignHandlerNew,signo:%d\n",iSeq,iSignNo);
}

int main()
{
char szBuf[8];
int iRet;
struct sigaction act;
act.sa_sigaction=SignHandlerNew;
act.sa_flags=SA_SIGINFO;
// 屏蔽掉SIGINT 信号,SigHandlerNew 将不能再捕捉SIGINT
sigset_t sigSet;
sigemptyset(&sigSet);
sigaddset(&sigSet,SIGINT);
sigprocmask(SIG_BLOCK,&sigSet,NULL);
//
sigemptyset(&act.sa_mask);
sigaction(SIGINT,&act,NULL);
sigaction(SIGQUIT,&act,NULL);
do{
iRet=read(STDIN_FILENO,szBuf,sizeof(szBuf)-1);
if(iRet<0){
perror("read fail.");
break;
}
szBuf[iRet]=0;
printf("Get: %s",szBuf);
}while(strcmp(szBuf,"quit\n")!=0);
return 0;
}

4. 用程序发送信号
4.1. kill信号发送函数
原型为:
#include
#include
int kill(pid_t pid, int sig);
参数pid为将要接受信号的进程的pid。 对kill()的pid,有如下描述:
    pid > 0 将信号发送给ID为pid的进程
    pid == 0 将信号发送给与发送进程属于同意个进程组的所有进程

    pid < 0 将信号发送给进程组ID等于pid绝对值的所有进程

    pid == -1 将信号发送给该进程有权限发送的系统里的所有进程
参数sig为要发送的信号
如果成功,返回0,否则为-1。

有信号的发送都要先经过权限检查,如果进程没有相应发送的权限,kill()会出错返回,并把errno设为EPERM。但也有一个例外,对SIGCONT,进程可以将它发送给当前会话的所有进程。
示例,输入结束后,将通过发送信号SIGQUIT把自己杀掉:
#include
#include
#include
#include
int main()
{
while(true){
if(getchar()==EOF)
kill(getpid(),SIGQUIT);
}
return 0;
}
4.2. sigqueue信号发送函数
sigqueue也可以发送信号,并且能传递附加的信息。
原型为:
#include
int sigqueue(pid_t pid, int sig, const union sigval value);
参数pid为接收信号的进程;
参数sig为要发送的信号;
参数value为一整型与指针类型的联合体:
union sigval {
int   sival_int;
void *sival_ptr;
};
由sigqueue函数发送的信号的第3个参数value的值,可以被进程的信号处理函数的第2个参数info->si_ptr接收到。
示例1,进程给自己发信号,并且带上附加信息:
#include
#include
#include
#include
#include
#include

void SignHandlerNew(int signum,siginfo_t *info,void *myact)
{
char *pszInfo=(char *)(info->si_ptr);
printf("Get:%d info:%s\n",signum,pszInfo);
}

int main(int argc,char**argv)
{
struct sigaction act;  
union sigval mysigval;
int sig;
char data[]="other info";
//
if(argc<2){
printf("usage: SIGNNUM\n");
return -1;
}
mysigval.sival_ptr=data;
sig=atoi(argv[1]);
sigemptyset(&act.sa_mask);
act.sa_sigaction=SignHandlerNew;
act.sa_flags=SA_SIGINFO;
sigaction(sig,&act,NULL);
while(true){
printf("wait for the signal\n");
sigqueue(getpid(),sig,mysigval);
sleep(2);
}
}

示例2:一个进程向另外一个进程发送信号。注意:发送进程不要将自己进程空间的地址发送给接收进程,因为接收进程接收到地址也访问不到发送进程的地址空间的。

示例2信号接收程序:
#include
#include
#include
#include
#include
#include

void SignHandlerNew(int signum,siginfo_t *info,void *myact)
{
printf("Get:%d info:%d\n",signum,info->si_int);
}

int main(int argc,char**argv)
{
struct sigaction act;
//
if(argc<2){
printf("usage: signnum\n");
return -1;
}
sigemptyset(&act.sa_mask);
act.sa_sigaction=SignHandlerNew;
act.sa_flags=SA_SIGINFO;
sigaction(atoi(argv[1]),&act,NULL);
while(1)
{
printf("wait for the signal\n");
sleep(2);
}
return 0;
}

示例2信号发送程序:
#include
#include
#include
#include
#include
#include

int main(int argc,char**argv)
{
union sigval mysigval;
int iPid,iSignNo,iData;
//
if(argc<4){
printf("usage: pid signnum data\n");
return -1;
}
iPid=atoi(argv[1]);
iSignNo=atoi(argv[2]);
iData=atoi(argv[3]);
mysigval.sival_int=iData;
if(sigqueue(iPid,iSignNo,mysigval)<0)
perror("Send signal fail.");
return 0;
}

4.3 信号排队
如果进程阻塞了一个信号,在没有对其解除阻塞之前,该信号产生了多次,将会如何处理呢?Linux并不会对信号排队,当信号解除阻塞后,内核只向进程递送一个信号,而不管在其阻塞期间有多少个信号产生。


4.4信号未决(pending)

信号是由某些事件产生的,这些事件可能是硬件异常(如被零除),软件条件(如计时器超时),终端信号或调用kill()/raise()函数。信号产生时,内核通常会在进程表中设置某种标志,表示当前信号的状态。当内核对信号采取某种动作时,我们说向进程递送(deliver)了一个信号,而在信号产生和递送之间的间隔内,该信号的状态是未决的(pending)。

获得未决的信号sigpending

int sigpending(sigset_t * set);

该函数在set中返回进程中当前尚未递送的信号集。

示例程序:
#include
#include
#include
#include
#include

int main(void)
{
  sigset_t set,pendset;
  struct sigaction action;
  //清空信号集
  sigemptyset(&set);        
  //加入SIGTERM呢个信号      
  sigaddset(&set,SIGTERM);           
  //设为阻塞
  sigprocmask(SIG_BLOCK,&set,NULL);     
  //因为阻塞所以就算用kill发送信号都无反应
  kill(getpid(),SIGTERM); 
  //查下有什么阻塞信号,装入&pendset里面
   sigpending(&pendset);  
  //看下面有什么信号在里面
  if(sigismember(&pendset,SIGTERM))     
  {
    printf("yes,the SIGTERM is here\n");
    //清空阻塞信号集
    sigemptyset(&action.sa_mask); 
    //信号涵数处理为ignore(忽略)       
    action.sa_handler=SIG_IGN;        
    //启动同signal功能类的信号涵数 
    sigaction(SIGTERM,&action,NULL);     
  }
  //解除之前的信号阻塞
  sigprocmask(SIG_UNBLOCK,&set,NULL);      
  exit(EXIT_SUCCESS);
}

5. 计时器与信号
5.1. 睡眠函数
Linux下有两个睡眠函数,原型为:
#include
unsigned int sleep(unsigned int seconds);
void usleep(unsigned long usec);
函数sleep让进程睡眠seconds秒,函数usleep让进程睡眠usec毫秒。
sleep 睡眠函数内部是用信号机制进行处理的,用到的函数有:
    #include
    产生时钟信号SIGALRM的alarm函数

    unsigned int alarm(unsigned int seconds);     // 告知自身进程,要进程在seconds秒后自动产生一个SIGALRM的信号,
        alarm()函数可设置一个计时器,计时器超时就产生SIGALRM信号。由于每个进程只能有一个SIGALRM处理程序,所以只能为一个进程设置一个计时器,
    所以alarm()和setitimer()会共享同一个SIGALRM信号和该信号的处理函数。也就是说,alarm()和setitimer()彼此会互相影响。调用alarm(),会使先前设置的计时器失效,
    并把没有超时的时间作为当前alarm的返回值。如先前设置的时钟为100秒,当前调用alarm()时才经过30秒,剩余的70秒就作为alarm()的返回值,
    并用alarm()中指定的秒数重    新设置计时器。如果seconds为0,则会取消先前设置的计时器,并将其余留值作为alarm()的返回值。

    等待信号的pause函数

    int pause(void);        // 将自身进程挂起,直到有信号发生时才从pause返回

    pause()会使当前进程挂起,直到捕捉到一个信号,对指定为忽略的信号,pause()不会返回。只有执行了一个信号处理函数,并从其返回,puase()才返回-1,
    并将errno设为EINTR。
示例:模拟睡眠3秒:
#include
#include
#include

void SignHandler(int iSignNo)
{
printf("signal:%d\n",iSignNo);
}

int main()
{
signal(SIGALRM,SignHandler);
alarm(3);
printf("Before pause().\n");
pause();
printf("After pause().\n");
return 0;
}
注意:因为sleep在内部是用alarm实现的,所以在程序中最好不要sleep与 alarm混用,以免造成混乱。
5.2. 时钟处理  
Linux为每个进程维护3个计时器,分别是真实计时器、虚拟计时器和实用计时器。
l         真实计时器计算的是程序运行的实际时间;
l         虚拟计时器计算的是程序运行在用户态时所消耗的时间(可认为是实际时间减掉(系统调用和程序睡眠所消耗)的时间);
l         实用计时器计算的是程序处于用户态和处于内核态所消耗的时间之和。
例如:有一程序运行,在用户态运行了5秒,在内核态运行了6秒,还睡眠了7秒,则真实计算器计算的结果是18秒,虚拟计时器计算的是5秒,实用计时器计算的是11秒。
用指定的初始间隔和重复间隔时间为进程设定好一个计时器后,该计时器就会定时地向进程发送时钟信号。3个计时器发送的时钟信号分别为:SIGALRM,SIGVTALRM和SIGPROF。
用到的函数与数据结构:
#include

//获取计时器的设置
//which指定哪个计时器,可选项为ITIMER_REAL(真实计时器)、ITIMER_VITUAL(虚拟计时器、ITIMER_PROF(实用计时器))
//value为一结构体的传出参数,用于传出该计时器的初始间隔时间和重复间隔时间
//如果成功,返回0,否则-1
int getitimer(int which, struct itimerval *value);

//设置计时器
//which指定哪个计时器,可选项为ITIMER_REAL(真实计时器)、ITIMER_VITUAL(虚拟计时器、ITIMER_PROF(实用计时器))
//value为一结构体的传入参数,指定该计时器的初始间隔时间和重复间隔时间
//ovalue为一结构体传出参数,用于传出以前的计时器时间设置。
//如果成功,返回0,否则-1
int setitimer(int which, const struct itimerval *value, struct itimer val *ovalue);

struct itimerval {
struct timeval it_interval; /* next value */            // 重复间隔
struct timeval it_value;    /* current value */     // 初始间隔    
};
struct timeval {
long tv_sec;                /* seconds */                    // 时间的秒数部分
long tv_usec;               /* microseconds */        // 时间的微秒部分
};

示例:启用真实计时器的进行时钟处理
#include
#include
#include
#include

void TimeInt2Obj(int imSecond,timeval *ptVal)
{
ptVal->tv_sec=imSecond/1000;
ptVal->tv_usec=(imSecond%1000)*1000;
}

void SignHandler(int SignNo)
{
printf("Clock\n");
}

int main()
{
signal(SIGALRM,SignHandler);
itimerval tval;
TimeInt2Obj(1,&tval.it_value);            // 设初始间隔为1毫秒,注意不要为0
TimeInt2Obj(1500,&tval.it_interval);    // 设置以后的重复间隔为1500毫秒
setitimer(ITIMER_REAL,&tval,NULL);
while(getchar()!=EOF);
return 0;
}

最后的忠告:千万不要去写一个复杂的信号处理程序,那是最出力不讨好的事情,信号处理程序每多一行,你的程序莫名崩溃的可能性就增大一分,谁能保证你在信号处理函数里调用的都是可重入函数呢?本文中的很多例程使用的printf()就不是可重入的……信号处理程序应该尽量简单,对稍微复杂的任务,应该想办法(如siglongjmp(),设置全局标志等)交给主程序处理。

阅读(682) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~