忘了说明: setitimer一个进程中只能有一个 下一个会覆盖前一个的定时 想一个进程多个定时器只能自己实现。
setitimer() 不支持在同一进程中同时使用多次以支持多个定时器。
linux有关定时器(setitimer)的叙述是这样的:
linux系统给每个进程提供了3个定时器,每个定时器在各自不同的域里面计数。当任何一个timer计数到结束了,系统就发送一个信号(signal)给该进程,同时计数器重置。
一共支持以下3中计数器形式:
ITIMER_REAL 在real time中计数器减1,然后等计数往比后发送SIGALRM信号。
ITIMER_VIRTUAL 当进程在执行的过程中计数,然后当计数完毕后发送SIGVTALRM信号给该进程。
ITIMER_PROF 在该进程被执行和系统在代表该进程执行的时间都进行计数
【getitimer/setitimer系统调用】
功能描述:
获取或设定间歇计时器的值。系统为进程提供三种类型的计时器,每一类以不同的时间域递减其值。当计时器超时,信号被发送到进程,之后计时器重启动。
用法:
#include
int getitimer(int which, struct itimerval *value);
int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue);
参数:
which:间歇计时器类型,有三种选择
ITIMER_REAL //数值为0,计时器的值实时递减,发送的信号是SIGALRM。
ITIMER_VIRTUAL //数值为1,进程执行时递减计时器的值,发送的信号是SIGVTALRM。
ITIMER_PROF //数值为2,进程和系统执行时都递减计时器的值,发送的信号是SIGPROF。
value,ovalue:时间参数,原型如下
struct itimerval
{
struct timeval it_interval;
struct timeval it_value;
};
struct timeval
{
long tv_sec;
long tv_usec;
};
setitimer()将value指向的结构体设为计时器的当前值,如果ovalue不是NULL,将返回计时器原有值。
返回说明:
成功执行时,返回0。失败返回-1,errno被设为以下的某个值
EFAULT:value或ovalue是不有效的指针
EINVAL:其值不是ITIMER_REAL,ITIMER_VIRTUAL 或 ITIMER_PROF之一
#include
#include
#include
#include
#include
#include
void PrintMsg(int Num)
{
printf("%s\n", "Hello World");
return;
}
int main(int argc, char* argv[])
{
signal(SIGALRM, PrintMsg);
struct itimerval tick;
tick.it_value.tv_sec = 10; //十秒钟后将启动定时器
tick.it_value.tv_usec = 0;
tick.it_interval.tv_sec =1; //定时器启动后,每隔1秒将执行相应的函数
tick.it_interval.tv_usec = 0;
//setitimer将触发SIGALRM信号
int ret = setitimer(ITIMER_REAL, &tick, NULL);
if ( ret != 0)
{
printf("Set timer error. %s \n", strerror(errno) );
return -1;
}
printf("Wait!\n");
getchar();
return 0;
}
#include
#include
#include
#include
#include
/*设置定时器到达时间*/
#define REFRESH_TIME 10
/*设置定时器*/
void set_refresh_time(int sec)
{
struct itimerval nvalue;
nvalue.it_value.tv_sec = sec;
nvalue.it_value.tv_usec = 0;
nvalue.it_interval.tv_sec = sec;//每隔sec时间触发一次定时器
nvalue.it_interval.tv_usec = 0;
/*此处使用的是ITIMER_REAL,所以对应的是SIGALRM信号*/
setitimer(ITIMER_REAL, &nvalue, NULL);
}
/*定时器处理函数*/
void timer_handler(int msg)
{
switch(msg) {
case SIGALRM:
printf("to do someting when set timer is comming\n");
break;
default:
break;
}
return;
}
int main(int argc, char **argv)
{
int value;
/*设置SIGALRM信号的处理函数,即定时器处理函数*/
signal(SIGALRM, timer_handler);
if(!argv[1]) {
Printf("use default time %d\n", REFRESH_TIME);
/*设置定时器*/
set_refresh_time(REFRESH_TIME);
} else {
value = atoi(argv[1]);
set_refresh_time(value);
}
while(1) {
}
}
int msSleep(long ms) {
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = ms;
return select(0, NULL, NULL, NULL, &tv);
}
这样使用的好处:
int main(int argc, char **argv)
{
int sock;
FILE *fp;
struct fd_set fds;
struct timeval timeout={3,0}; //select等待3秒,3秒轮询,要非阻塞就置0
char buffer[256]={0}; //256字节的接收缓冲区
while(1)
{
FD_ZERO(&fds); //每次循环都要清空集合,否则不能检测描述符变化
FD_SET(sock,&fds); //添加描述符
FD_SET(fp,&fds); //同上
maxfdp=sock>fp?sock+1:fp+1; //描述符最大值加1
switch(select(maxfdp,&fds,&fds,NULL,&timeout)) //select使用
{
case -1: exit(-1);break; //select错误,退出程序
case 0:break; //再次轮询,timeout
default:
if(FD_ISSET(sock,&fds)) //测试sock是否可读,即是否网络上有数据
{
recvfrom(sock,buffer,256,.....);//接受网络数据
if(FD_ISSET(fp,&fds)) //测试文件是否可写
fwrite(fp,buffer...);//写入文件
buffer清空;
}
break; // end if break;
}
}
}
今天看书看到了关于alarm的一些用法,自己有在网上找了些资料看了下;1。alarm()执行后,进程将继续执行,在后期(alarm以后)的执行过程中将会在seconds秒后收到信号SIGALRM并执行其处理函数。
#include
#include
#include
void sigalrm_fn(int sig)
{
printf("alarm!\n");
alarm(2);
return;
}
int main(void)
{
signal(SIGALRM, sigalrm_fn);
alarm(1);
while(1) pause();
}
2.alarm定时器,但是只能精确到秒,然而我们如果需要用到更精准的怎么办?经过群里的大牛知道,看了下可以用setitimer int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue)); setitimer()比alarm功能强大,支持3种类型的定时器: ITIMER_REAL : 以系统真实的时间来计算,它送出SIGALRM信号。 ITIMER_VIRTUAL : -以该进程在用户态下花费的时间来计算,它送出SIGVTALRM信号。 ITIMER_PROF : 以该进程在用户态下和内核态下所费的时间来计算,它送出SIGPROF信号。 setitimer()第一个参数which指定定时器类型(上面三种之一);第二个参数是结构itimerval的一个实例;第三个参数可不做处理。 setitimer()调用成功返回0,否则返回-1。
下面是关于setitimer调用的一个简单示范,在该例子中,每隔一秒发出一个SIGALRM,每隔0.5秒发出一个SIGVTALRM信号:
#include
#include
#include
#include
#include
#include
int sec;
void sigroutine(int signo){
switch (signo){
case SIGALRM:
printf("Catch a signal -- SIGALRM \n");
signal(SIGALRM, sigroutine);
break;
case SIGVTALRM:
printf("Catch a signal -- SIGVTALRM \n");
signal(SIGVTALRM, sigroutine);
break;
}
return;
}
int main()
{
struct itimerval value, ovalue, value2; //(1)
sec = 5;
printf("process id is %d\n", getpid());
signal(SIGALRM, sigroutine);
signal(SIGVTALRM, sigroutine);
value.it_value.tv_sec = 1;
value.it_value.tv_usec = 0;
value.it_interval.tv_sec = 1;
value.it_interval.tv_usec = 0;
setitimer(ITIMER_REAL, &value, &ovalue); //(2)
value2.it_value.tv_sec = 0;
value2.it_value.tv_usec = 500000;
value2.it_interval.tv_sec = 0;
value2.it_interval.tv_usec = 500000;
setitimer(ITIMER_VIRTUAL, &value2, &ovalue);
for(;;)
;
}
setitimer不会引起线程的阻塞、也不会引起线程的切换动作,就是简单的启动一个定时器,开始定时,而且这种定时应该是基于内核的,(windwos的settimer是基于一种消息的模型);setitimer虽然有三种类型ITIMER_REAL,ITIMER_VIRTUAL ITIMER_PROF,但是在同一时间同一进程,一种类型的只能有1个setitimer;如果我们需要多个定时器怎么办?3.
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main(int argc, char **argv)
{
unsigned int nTimeTestSec = 0;
unsigned int nTimeTest = 0;
struct timeval tvBegin;
struct timeval tvNow;
int ret = 0;
unsigned int nDelay = 0;
struct timeval tv;
int fd = 1;
int i = 0;
struct timespec req;
unsigned int delay[20] =
{500000, 100000, 50000, 10000, 1000, 900, 500, 100, 10, 1, 0};
int nReduce = 0; //误差
fprintf(stderr, "%19s%12s%12s%12s\n", "fuction", "time(usec)", "realtime", "reduce");
fprintf(stderr, "----------------------------------------------------\n");
for (i = 0; i < 20; i++)
{
if (delay[i] <= 0)
break;
nDelay = delay[i];
//test sleep
gettimeofday(&tvBegin, NULL);
ret = usleep(nDelay);
if(ret == -1)
{
fprintf(stderr, "usleep error, errno=%d [%s]\n", errno, strerror(errno));
}
gettimeofday(&tvNow, NULL);
nTimeTest = (tvNow.tv_sec - tvBegin.tv_sec) * 1000000 + tvNow.tv_usec - tvBegin.tv_usec;
nReduce = nTimeTest - nDelay;
fprintf (stderr, "\t usleep %8u %8u %8d\n", nDelay, nTimeTest,nReduce);
//test nanosleep
req.tv_sec = nDelay/1000000;
req.tv_nsec = (nDelay%1000000) * 1000;
gettimeofday(&tvBegin, NULL);
ret = nanosleep(&req, NULL);
if (-1 == ret)
{
fprintf (stderr, "\t nanousleep %8u not support\n", nDelay);
}
gettimeofday(&tvNow, NULL);
nTimeTest = (tvNow.tv_sec - tvBegin.tv_sec) * 1000000 + tvNow.tv_usec - tvBegin.tv_usec;
nReduce = nTimeTest - nDelay;
fprintf (stderr, "\t nanosleep %8u %8u %8d\n", nDelay, nTimeTest,nReduce);
//test select
tv.tv_sec = 0;
tv.tv_usec = nDelay;
gettimeofday(&tvBegin, NULL);
ret = select(0, NULL, NULL, NULL, &tv);
if (-1 == ret)
{
fprintf(stderr, "select error. errno = %d [%s]\n", errno, strerror(errno));
}
gettimeofday(&tvNow, NULL);
nTimeTest = (tvNow.tv_sec - tvBegin.tv_sec) * 1000000 + tvNow.tv_usec - tvBegin.tv_usec;
nReduce = nTimeTest - nDelay;
fprintf (stderr, "\t select %8u %8u %8d\n", nDelay, nTimeTest,nReduce);
//pselcet
req.tv_sec = nDelay/1000000;
req.tv_nsec = (nDelay%1000000) * 1000;
gettimeofday(&tvBegin, NULL);
ret = pselect(0, NULL, NULL, NULL, &req, NULL);
if (-1 == ret)
{
fprintf(stderr, "select error. errno = %d [%s]\n", errno, strerror(errno));
}
gettimeofday(&tvNow, NULL);
nTimeTest = (tvNow.tv_sec - tvBegin.tv_sec) * 1000000 + tvNow.tv_usec - tvBegin.tv_usec;
nReduce = nTimeTest - nDelay;
fprintf (stderr, "\t pselect %8u %8u %8d\n", nDelay, nTimeTest,nReduce);
fprintf (stderr, "--------------------------------\n");
}
return 0;
}
int msSleep(long ms) {
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = ms;
return select(0, NULL, NULL, NULL, &tv);
}
上面这段代码作者有这样的话