Linux中alarm函数和信号处理

alarm函数作用是设定一个计时器,到时间后会向进程发送信号。
它向进程发送SIGALRM信号。要注意的是,一个进程只能有一个闹钟时间,如果在调用alarm之前已设置过闹钟时间,则任何以前的闹钟时间都被新值所代替。
我们还可以调用alarm(0)来取消此闹钟,并返回剩余时间。

函数原型:
unsigned int alarm(unsigned int seconds);
函数参数:
seconds:指定的秒数

例子程序:test7.c

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <errno.h>

typedef struct
{
	long mtype;
	char mbuf[1024];
}mymsg;

void SigAlarmProc()
{
	printf("2222\n");
	return;
}

int main(void)
{
	int i, j;
	int time = 5;
	mymsg message;
	
	sigset(SIGALRM, SigAlarmProc);//注册信号处理函数
	alarm(time);//设置闹钟函数
	
	printf("1111\n");
	i = msgget(1004, 0666|IPC_CREAT);
	j = msgrcv(i, (void *)&message, 1024, 0, 0);
	if (j < 0)
	{
		printf("rev error, errno=%d\n", errno);
		//exit(-1);
	}
	else
	{
		//显示取出的消息
		printf("%s\n", message.mbuf);
	}
	
	printf("after msgcrv()\n");
	
	alarm(0);
	printf("3333\n");
	
	return 0;
}


执行结果:

[root@server ~]# ./test7
1111
2222
rev error, errno=4
after msgcrv()
3333

程序执行过程:
先在sigset注册信号处理函数,然后设置闹钟函数,然后打印1111。
在msgrcv()处阻塞,5秒后alarm时间到了,跳转到SigAlarmProc,打印2222。
然后发现消息队列被信号处理中断了,没有继续处于接收的阻塞状态,打印了errno=4。网上说“msgsnd/msgrcv将返回-1,errno被设置为EINTR(-4)”。
随后继续执行,打印后两句提示。

一般来说信号处理是一种中断机制,执行完信号处理函数后,程序会回到中断的地方继续执行下去。
网上找了一下:

对于linux的信号,如果用户自己注册了某信号的处理函数,那么他的执行过程是这样的。
1.收到该信号后马上进入内核态
2.内核将进入内核前的用户现场(寄存器、标志位等)保存,具体是建立一个数据结构(《linux内核源码》一书中称为“原始框架”)
3.调用该信号的处理函数
4.将用户现场恢复,继续执行

例子程序:test8.c

#include <unistd.h>
#include <stdio.h>
#include <signal.h>

void SigAlarmProc()
{
	printf("2222\n");
	return;
}

int main(void)
{
	int i;
	int time = 5;
	
	sigset(SIGALRM, SigAlarmProc);//注册信号处理函数
	alarm(time);//设置闹钟函数
	
	printf("1111\n");

	for (i = 0; i < 10; i++)
	{
		printf("i=%d\n", i);
		sleep(1);
	}
	
	alarm(0);
	printf("3333\n");
	
	return 0;
}


执行结果:

[root@server ~]# ./test8
1111
i=0
i=1
i=2
i=3
i=4
2222
i=5
i=6
i=7
i=8
i=9
3333

说明:
先注册信号处理函数,设置闹钟函数,打印1111。
在for循环中,打印一个i的值,然后睡眠1秒钟。
我们可以看到在i=4时,闹钟函数发出了信号,然后执行了SigAlarmProc,打印2222。
SigAlarmProc执行完后又继续回到for循环打印i的值,然后退出。

所以一般情况下,进程收到信号,执行完信号处理函数会回到中断的地方继续执行程序。

忽略信号
如果想忽略某个信号,使用:

sigset(SIGALRM, SIG_IGN);

参考资料:
http://blog.csdn.net/dongzhongshu/article/details/6270261
http://blog.chinaunix.net/uid-20558821-id-2801753.html
http://blog.csdn.net/mwx1234/article/details/7189791