c实现的几种定时器

原文:https://www.cnblogs.com/BKjungle/p/6127807.html


1.linux下调用系统函数alarm(),setitimer(),sleep(),usleep()(实现微妙定时),

2.单纯c语言实现gettimeofday()(微妙定时),time(),

3.windows可用Sleep()实现微秒级定时

4 IO复用的  select 函数 实现

1.alarm()

#include 

unsigned int alarm(unsigned int seconds);

函数返回值

成功:如果调用此alarm()前,进程已经设置了闹钟时间,则返回上一个闹钟时间的剩余时间,否则返回0。不阻塞!!!

出错:-1

作用:  调用 alarm 函数即设定一个闹钟,也就是告诉内核在 seconds 秒之后给当前进程发 SIGALRM 信号,默认处理动作是终止当前进程。闹钟返

回值是 0 或者是以前设定的闹钟时间还余下的秒数。如果 seconds 值为 0,表示取消以前设定的闹钟,函数的返回值仍然

是以前设定的闹钟时间还余下的秒数。

------ alarm

#include 

#include 

int main(void)

{

int counter;

alarm(1);

for(counter=0; 1; counter++)

printf("counter=%d ", counter);

return 0;

}

这个程序的作用是 1 秒钟之内不停地数数,1 秒钟到了就被 SIGALRM 信号终

止。


-----------》》1.1 配合pause函数实现sleep函数!

#include 

int pause(void);

pause 函数使调用进程挂起直到有信号递达。如果信号的处理动作是终止进程,则进程终止,pause 函数没有机会返回;如果信号的处理动作是忽略,则

进程继续处于挂起状态,pause 不返回;如果信号的处理动作是捕捉,则调用了信号处理函数之后 pause 返回-1,errno 设置为 EINTR,所以 pause 只有出错

的返回值(想想以前还学过什么函数只有出错返回值?)。错误码 EINTR 表示“被信号中断”。

下面我们用 alarm 和 pause 实现 sleep(3)函数,称为 mysleep。

mysleep

#include 

#include 

#include 

void sig_alrm(int signo)

{

/* nothing to do */

}

unsigned int mysleep(unsigned int nsecs)

{

struct sigaction newact, oldact;

unsigned int unslept;

newact.sa_handler = sig_alrm;

sigemptyset(&newact.sa_mask);

newact.sa_flags = 0;

sigaction(SIGALRM, &newact, &oldact);

alarm(nsecs);

pause();

unslept = alarm(0);

sigaction(SIGALRM, &oldact, NULL);

return unslept;

}

int main(void)

{

while(1){

mysleep(2);

printf("Two seconds passed\n");

}

return 0;

}

1. main 函数调用 mysleep 函数,后者调用 sigaction 注册了 SIGALRM 信号        的处理函数 sig_alrm。

2. 调用 alarm(nsecs)设定闹钟。

3. 调用 pause 等待,内核切换到别的进程运行。

4. nsecs 秒之后,闹钟超时,内核发 SIGALRM 给这个进程。

5. 从内核态返回这个进程的用户态之前处理未决信号,发现有 SIGALRM 信号,其处理函数是 sig_alrm。

6. 切换到用户态执行 sig_alrm 函数,进入 sig_alrm 函数时 SIGALRM 信号被自动屏蔽,从 sig_alrm 函数返回时 SIGALRM 信号自动解除屏蔽。然后

自动执行系统调用 sigreturn 再次进入内核,再返回用户态继续执行进程的主控制流程(main 函数调用的 mysleep 函数)。

7. pause 函数返回-1,然后调用 alarm(0)取消闹钟,调用 sigaction 恢复SIGALRM 信号以前的处理动作。




2.setitimer()----------------------- 

常用到的函数:

#include 

int getitimer (int which, struct itimerval* value);

int setitimer (int which, struct itimerval* newvalue, struct itimerval* oldvalue);

which有三种状态:

ITIMER_REAL: 对指定时间值,按自然时间计数, 时间到发出SIGALRM信号.

ITIMER_VIRTUAL: 对指定时间值, 当只在用户态时(进程执行的时候)计数,  时间到发出SIGVTALRM信号.

ITIMER_PROF: 对指定时间值, 用户态或内核态(进程执行与系统为进程调度)都计数, 时间到, 发出SIGPROF信号, 与ITIMER_VIRTVAL联合, 常用来计算系统内核时间和用户时间.

struct timeval

{

long tv_sec;/* 秒 */

long tv_usec;/* 微秒 */

};

struct itimerval

{

struct timeval it_interval; /* 时间间隔 *///循环定时时间

struct timeval it_value;/* 当前时间计数 */第一次计时时间

};

it_interval用来指定每隔多长时间执行任务, it_value用来保存当前时间离执行任务还有多长时间. 比如说, 你指定it_interval为2秒(微秒为0), 开始的时候我们把it_value的时间也设定为2秒(微秒为0), 当过了一秒, it_value就减少一个为1, 再过1秒, 则it_value又减少1, 变为0, 这个时候发出信号(告诉用户时间到了, 可以执行任务了), 并且系统自动把it_value的置重置为it_interval的值, 即2秒, 再重新计数.


 -------------------------------代码实现

#include

#include

#include

#include

#include

#include

static char msg[] = "time is running out.\n";

static int len;

/* time's up */

void prompt_info (int signo)

{

write (STDERR_FILENO, msg, len);

}

void init_sigaction (void)

{

struct sigaction tact;

tact.sa_handler = prompt_info;

tact.sa_flags = 0;

sigemptyset (&tact.sa_mask);

sigaction (SIGALRM, &tact, NULL);

}

void init_time ()

{

struct itimerval value;

value.it_value.tv_sec = 2;

value.it_value.tv_usec = 0;

value.it_interval = value.it_value;

/* set ITIMER_REAL */

setitimer (ITIMER_REAL, &value, NULL);

}

int main (int argc, char** argv)

{

len = strlen (msg);

init_sigaction ();

init_time ();

while (1);

exit (0);

}

该程序的ITMER_REAL定时器,每隔2秒钟都会发送一个SIGALRM信号,当主函数接收到了这个信号之后,调用信号处理函数prompt_info在标准错误上输出time is running out这个字符串。

对于ITIMER_VIRTUAL和ITIMER_PROF的使用方法类似,当你在setitimer里面设置的定时器为ITIMER_VIRTUAL的时候,你把sigaction里面的SIGALRM改为SIGVTALARM, 同理,ITIMER_PROF对应SIGPROF。

不过,你可能会注意到,当你用ITIMER_VIRTUAL和ITIMER_PROF的时候,你拿一个秒表,你会发现程序输出字符串的时间间隔会不止2秒,甚至5-6秒才会输出一个,那是因为cpu在用户与内核切换之间也会浪费时间,这段时间是不计入在指定时间范围之内的。


3.time()或gettimeofday()利用时间差来计算--------------------------------------------------------------------------------------------------------------------------


1. #include 

2. #include 

3. #include 

4. #include 

5. #include  //包含time()函数

6.#include <sys/time.h>//包含gettimeofday()函数

7. staticchar msg[] = "I received a msg.\n"; 

8. int len;

9. static time_t lasttime;

10. void show_msg(int signo)

11. {

12.     write(STDERR_FILENO, msg, len);

13. }

14. intmain()

15. {

16.     structsigaction act;

17.     unionsigval tsval;

18.

19.     act.sa_handler = show_msg;

20.     act.sa_flags = 0;

21.     sigemptyset(&act.sa_mask);

22.     sigaction(50, &act, NULL);

23.

24.     len = strlen(msg);

25.     time(&lasttime);

26.     while( 1 )

27.     {

28.         time_tnowtime;

29.         /*获取当前时间*/

30.         time(&nowtime);

31.         /*和上一次的时间做比较,如果大于等于2秒,则立刻发送信号*/

32.         if(nowtime - lasttime >= 2)

33.         {

34.             /*向主进程发送信号,实际上是自己给自己发信号*/

35.             sigqueue(getpid(), 50, tsval);

36.             lasttime = nowtime;

37.         }

38.     }

39.     return0;

40. }


如果你想更精确的计算时间差,你可以把 time 函数换成gettimeofday,这个可以精确到微妙。

上面介绍的几种定时方法各有千秋,在计时效率上、方法上和时间的精确度上也各有不同,采用哪种方法,就看你程序的需要


4 sleep实现方法-------------------- 

下面我们来看看用sleep以及usleep怎么实现定时执行任务。

下载: timer2.c

1. #include 

2. #include 

3. #include 

4. #include 

5.

6. staticchar msg[] = "I received a msg.\n";

7. int len;

8. void show_msg(int signo)

9. {

10.     write(STDERR_FILENO, msg, len);

11. }

12. intmain()

13. {

14.     structsigaction act;

15.     unionsigval tsval;

16.

17.     act.sa_handler = show_msg;

18.     act.sa_flags = 0;

19.     sigemptyset(&act.sa_mask);

20.     sigaction(50, &act, NULL);

21.

22.     len = strlen(msg);

23.     while( 1 )

24.     {

25.         sleep(2); /*睡眠2秒*/

26.         /*向主进程发送信号,实际上是自己给自己发信号*/

27.         sigqueue(getpid(), 50, tsval);

28.     }

29.     return0;

30. }

看到了吧,这个要比上面的简单多了,而且你用秒表测一下,时间很准,指定2秒到了就给你输出一个字符串。所以,如果你只做一般的定时,到了时间去执行一个任务,这种方法是最简单的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,076评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,658评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,732评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,493评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,591评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,598评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,601评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,348评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,797评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,114评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,278评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,953评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,585评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,202评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,442评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,180评论 2 367
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,139评论 2 352

推荐阅读更多精彩内容

  • 信号的基本概念 信号被认为是一种软件中断(区别于硬件中断),信号机制提供了一种在单进程/线程下处理异步事件的方法。...
    小叶大孟阅读 1,907评论 0 1
  • 一、Linux系统概述 不加引号可理解为宏,直接替换,单引号中特殊字符会被解释为普通字符,双引号中$,,'还是特殊...
    赤果_b4a7阅读 1,500评论 0 2
  • 一、信号及信号来源 信号本质 信号是在软件层次上对中断机制的一种模拟,在原理上,一个进程收到一个信号与处理器收到一...
    丶Em1tu0F阅读 1,433评论 0 1
  • 信号信号是linux操作系统进程间通信的一种方式,一个应用进程可以接受、发送信号给另一个进程,当进程捕获到某个信号...
    zhile_doing阅读 465评论 0 0
  • 作业1:输出一个数最后一位 作业2:输出最大值 作业3 输出季节
    GXMCRH阅读 357评论 0 0