使用SIGALRM信号量和sleep冲突的解决办法 - 镇关西拳打鲁智深 - 博客频道 - CSDN.NET

# 镇关西拳打鲁智深

##

- 目录视图
- 摘要视图
- 订阅

2013年1月当选微软MVP名单揭晓！ 智能开发框架 JEECG 作者张代浩专访
2012CSDN博客之星火热出炉！ 2013年全国百所高校巡讲讲师招募

### 使用SIGALRM信号量和sleep冲突的解决办法

分类： 疑难问题 2011-09-28 16:32 1166人阅读 评论(3) 收藏 举报

系统中的一个模块需要频繁的获取系统时间，使用linux中内置的函数开销过大，因为需要的精度不是很高（毫秒级），索性用signal函数配合setitimer实现了个简易的全局时钟。

但是后来发现，SIGALRM的中断信号回终止sleep，因为sleep就是用SIGALRM信号量实现的，得另想方案。

这个替代方案就是POSIX中内置的定时器：timer_create()(创建)、timer_settime()(初始化)以及 timer_delete(销毁)，将自己的时间信号处理函数用timer_create注册为SIGUSR2，这样就不会中断sleep了。

以下内容转载自smart的BLOG，很详细。

POSIX定时器:timer_settime()

最强大的定时器接口来自POSIX时钟系列，其创建、初始化以及删除一个定时器的行动被分为三个不同的函数：timer_create()(创建定时器)、timer_settime()(初始化定时器)以及timer_delete(销毁它)。
创建一个定时器：

int timer_create(clockid_t clock_id, struct sigevent *evp, timer_t *timerid)

[cpp] view plaincopyprint?

1. struct itimespec{
2. struct timespec it_interval;
3. struct timespec it_value;
4. };

    struct itimespec{
        struct timespec it_interval;
        struct timespec it_value;
    };

进程可以通过调用timer_create()创建特定的定时器，定时器是每个进程自己的，不是在fork时继承的。clock_id说明定时器是基于哪 个时钟的，*timerid装载的是被创建的定时器的ID。该函数创建了定时器，并将他的ID 放入timerid指向的位置中。参数evp指定了定时器到期要产生的异步通知。如果evp为NULL，那么定时器到期会产生默认的信号，对 CLOCK_REALTIMER来说，默认信号就是SIGALRM。如果要产生除默认信号之外的其它信号，程序必须将 evp->sigev_signo设置为期望的信号码。struct sigevent 结构中的成员evp->sigev_notify说明了定时器到期时应该采取的行动。通常，这个成员的值为SIGEV_SIGNAL,这个值说明在 定时器到期时，会产生一个信号。程序可以将成员evp->sigev_notify设为SIGEV_NONE来防止定时器到期时产生信号。
如果几个定时器产生了同一个信号，处理程序可以用 evp->sigev_value来区分是哪个定时器产生了信号。要实现这种功能，程序必须在为信号安装处理程序时，使用struct sigaction的成员sa_flags中的标志符SA_SIGINFO。
clock_id取值为以下：

CLOCK_REALTIME :Systemwide realtime clock.
CLOCK_MONOTONIC:Represents monotonic time. Cannot be set.
CLOCK_PROCESS_CPUTIME_ID :High resolution per-process timer.
CLOCK_THREAD_CPUTIME_ID :Thread-specific timer.
CLOCK_REALTIME_HR :High resolution version of CLOCK_REALTIME.
CLOCK_MONOTONIC_HR :High resolution version of CLOCK_MONOTONIC.

[cpp] view plaincopyprint?

1. struct sigevent
3. {
4. int sigev_notify; //notification type
5. int sigev_signo; //signal number
6. union sigval sigev_value; //signal valu
7. void (*sigev_notify_function)(union sigval)
8. pthread_attr_t *sigev_notify_attributes
9. }
11. union sigval
13. {
15. int sival_int; //integer valu
16. void *sival_ptr; //pointer value
18. }

    struct sigevent

    {
        int sigev_notify; //notification type
        int sigev_signo; //signal number
        union sigval   sigev_value; //signal valu
        void (*sigev_notify_function)(union sigval)
        pthread_attr_t *sigev_notify_attributes
    }

    union sigval

    {

        int sival_int; //integer valu
        void *sival_ptr; //pointer value

    }

通过将evp->sigev_notify设定为如下值来定制定时器到期后的行为：
SIGEV_NONE：什么都不做，只提供通过timer_gettime和timer_getoverrun查询超时信息。
SIGEV_SIGNAL: 当定时器到期，内核会将sigev_signo所指定的信号传送给进程。在信号处理程序中，si_value会被设定会sigev_value。
SIGEV_THREAD: 当定时器到期，内核会(在此进程内)以sigev_notification_attributes为线程属性创建一个线程，并且让它执行sigev_notify_function，传入sigev_value作为为一个参数。
启动一个定时器：

timer_create()所创建的定时器并未启动。要将它关联到一个到期时间以及启动时钟周期，可以使用timer_settime()。

int timer_settime(timer_t timerid, int flags, const struct itimerspec *value, struct itimerspect *ovalue);

如同settimer()，it_value用于指定当前的定时器到期时间。当定时器到期，it_value的值会被更新成it_interval 的值。如果it_interval的值为0，则定时器不是一个时间间隔定时器，一旦it_value到期就会回到未启动状态。timespec的结构提供 了纳秒级分辨率：
[cpp] view plaincopyprint?

1. struct timespec{
3. time_t tv_sec;
5. long tv_nsec;
7. };

    struct timespec{

        time_t tv_sec;

        long tv_nsec;

    };

如果flags的值为TIMER_ABSTIME，则value所指定的时间值会被解读成绝对值(此值的默认的解读方式为相对于当前的时间)。这个经修改的行为可避免取得当前时间、计算“该时间”与“所期望的未来时间”的相对差额以及启动定时器期间造成竞争条件。
如果ovalue的值不是NULL，则之前的定时器到期时间会被存入其所提供的itimerspec。如果定时器之前处在未启动状态，则此结构的成员全都会被设定成0。

获得一个活动定时器的剩余时间：

int timer_gettime(timer_t timerid,struct itimerspec *value);

取得一个定时器的超限运行次数：

有可能一个定时器到期了，而同一定时器上一次到期时产生的信号还处于挂起状态。在这种情况下，其中的一个信号可能会丢失。这就是定时器超限。程序可 以通过调用timer_getoverrun来确定一个特定的定时器出现这种超限的次数。定时器超限只能发生在同一个定时器产生的信号上。由多个定时器， 甚至是那些使用相同的时钟和信号的定时器，所产生的信号都会排队而不会丢失。

int timer_getoverrun(timer_t timerid);

执行成功时，timer_getoverrun()会返回定时器初次到期与通知进程(例如通过信号)定时器已到期之间额外发生的定时器到期次数。举例来 说，在我们之前的例子中，一个1ms的定时器运行了10ms，则此调用会返回9。如果超限运行的次数等于或大于DELAYTIMER_MAX，则此调用会 返回DELAYTIMER_MAX。

执行失败时，此函数会返回-1并将errno设定会EINVAL，这个唯一的错误情况代表timerid指定了无效的定时器。

删除一个定时器：

int timer_delete (timer_t timerid);

一次成功的timer_delete()调用会销毁关联到timerid的定时器并且返回0。执行失败时，此调用会返回-1并将errno设定会 EINVAL，这个唯一的错误情况代表timerid不是一个有效的定时器。
例1：
[cpp] view plaincopyprint?

1. void handle()
2. {
3. time_t t;
4. char p[32];
6. time(&t);
7. strftime(p, sizeof(p), "%T", localtime(&t));
9. printf("time is %s\n", p);
10. }
14. int main()
15. {
16. struct sigevent evp;
17. struct itimerspec ts;
18. timer_t timer;
19. int ret;
21. evp.sigev_value.sival_ptr = &timer;
22. evp.sigev_notify = SIGEV_SIGNAL;
23. evp.sigev_signo = SIGUSR1;
24. signal(SIGUSR1, handle);
26. ret = timer_create(CLOCK_REALTIME, &evp, &timer);
27. if( ret )
28. perror("timer_create");
30. ts.it_interval.tv_sec = 1;
31. ts.it_interval.tv_nsec = 0;
32. ts.it_value.tv_sec = 3;
33. ts.it_value.tv_nsec = 0;
35. ret = timer_settime(timer, 0, &ts, NULL);
36. if( ret )
37. perror("timer_settime");
39. while(1);
40. }

    void handle()
    {
        time_t t;
        char p[32];

        time(&t);
        strftime(p, sizeof(p), "%T", localtime(&t));

        printf("time is %s\n", p);
    }

    int main()
    {
        struct sigevent evp;
        struct itimerspec ts;
        timer_t timer;
        int ret;

        evp.sigev_value.sival_ptr = &timer;
        evp.sigev_notify = SIGEV_SIGNAL;
        evp.sigev_signo = SIGUSR1;
        signal(SIGUSR1, handle);

        ret = timer_create(CLOCK_REALTIME, &evp, &timer);
        if( ret )
            perror("timer_create");

        ts.it_interval.tv_sec = 1;
        ts.it_interval.tv_nsec = 0;
        ts.it_value.tv_sec = 3;
        ts.it_value.tv_nsec = 0;

        ret = timer_settime(timer, 0, &ts, NULL);
        if( ret )
        perror("timer_settime");

        while(1);
    }

例2：
[cpp] view plaincopyprint?

1. void handle(union sigval v)
2. {
3. time_t t;
4. char p[32];
6. time(&t);
7. strftime(p, sizeof(p), "%T", localtime(&t));
9. printf("%s thread %lu, val = %d, signal captured.\n", p, pthread_self(), v.sival_int);
10. return;
11. }
15. int main()
16. {
17. struct sigevent evp;
18. struct itimerspec ts;
19. timer_t timer;
20. int ret;
22. memset (&evp, 0, sizeof (evp));
23. evp.sigev_value.sival_ptr = &timer;
24. evp.sigev_notify = SIGEV_THREAD;
25. evp.sigev_notify_function = handle;
26. evp.sigev_value.sival_int = 3; //作为handle()的参数
28. ret = timer_create(CLOCK_REALTIME, &evp, &timer);
29. if( ret)
30. perror("timer_create");
32. ts.it_interval.tv_sec = 1;
33. ts.it_interval.tv_nsec = 0;
34. ts.it_value.tv_sec = 3;
35. ts.it_value.tv_nsec = 0;
37. ret = timer_settime(timer, TIMER_ABSTIME, &ts, NULL);
38. if( ret )
39. perror("timer_settime");
41. while(1);
42. }

    void handle(union sigval v)
    {
        time_t t;
        char p[32];

        time(&t);
        strftime(p, sizeof(p), "%T", localtime(&t));

        printf("%s thread %lu, val = %d, signal captured.\n", p, pthread_self(), v.sival_int);
        return;
    }

    int main()
    {
        struct sigevent evp;
        struct itimerspec ts;
        timer_t timer;
        int ret;

        memset (&evp, 0, sizeof (evp));
        evp.sigev_value.sival_ptr = &timer;
        evp.sigev_notify = SIGEV_THREAD;
        evp.sigev_notify_function = handle;
        evp.sigev_value.sival_int = 3; //作为handle()的参数

        ret = timer_create(CLOCK_REALTIME, &evp, &timer);
        if( ret)
            perror("timer_create");

        ts.it_interval.tv_sec = 1;
        ts.it_interval.tv_nsec = 0;
        ts.it_value.tv_sec = 3;
        ts.it_value.tv_nsec = 0;

        ret = timer_settime(timer, TIMER_ABSTIME, &ts, NULL);
        if( ret )
            perror("timer_settime");

        while(1);
    }

分享到：
- 上一篇： C语言执行Linux的shell命令并获得返回值
- 下一篇： 使用WinSetupFromUSB来U盘安装windowsXP（不使用win PE系统）
查看评论

2楼 techon 2012-10-08 21:26发表 [回复] [引用] [举报]
  例2没问题，可能是例1都在一个线程里，例2是独立线程？

1楼 techon 2012-10-08 20:41发表 [回复] [引用] [举报]
  照样打断sleep啊。。。 我在main的while(1)里加入
  sleep(10);

Re: littlefang 2012-11-22 16:47发表 [回复] [引用] [举报]
  回复techon：我也发现了，，在有些内核版本里还是不行。。

您还没有登录,请[登录]或[注册]
* 以上用户言论只代表其个人观点，不代表CSDN网站的观点或立场

个人资料

littlefang

- 访问：27566次
- 积分：516分
- 排名：千里之外

- 原创：23篇
- 转载：6篇
- 译文：0篇
- 评论：9条

文章搜索

文章分类

- 备忘(9)
- 疑难问题(4)
- 一些思考(2)

文章存档

- 2012年12月(1)
- 2012年11月(1)
- 2012年05月(2)
- 2012年03月(4)
- 2011年11月(1)
- 2011年10月(1)
- 2011年09月(4)
- 2011年08月(1)
- 2011年07月(2)
- 2011年06月(1)
- 2011年04月(1)
- 2011年03月(2)
- 2011年02月(1)
- 2010年12月(4)
- 2010年10月(1)
- 2010年05月(2)
展开

阅读排行

- 使用WinSetupFromUSB来U盘安装windowsXP（不使用win PE系统）(6240)
- 没有atomic.h后如何在linux实现原子操作(2852)
- 海明码分组原则(1962)
- 在应用程序中替换Linux中Glibc的malloc的四种方法(1708)
- tcprewrite的安装和用法(1483)
- 深入Linux的内存管理，关于PTMalloc3、Hoard和TCMalloc(1340)
- C语言执行Linux的shell命令并获得返回值(1321)
- 工商银行网银U盾在win7 64位操作系统无法使用的问题(1220)
- 使用SIGALRM信号量和sleep冲突的解决办法(1166)
- linux的pthread_self与gettid的返回值和开销的区别(847)

评论排行

- 使用SIGALRM信号量和sleep冲突的解决办法(3)
- winpcap/wireshark在win7 64位系统下IP头部结构长度为0的bug(2)
- linux中下载ftp文件的几个方法(2)
- tcprewrite的安装和用法(1)
- Linux动态链接库同名符号装载问题（一）(1)
- valgrind安装过程中无法定位libmpi.a中lam_mpi_commworld错误的解决办法。(0)
- C语言执行Linux的shell命令并获得返回值(0)
- 使用WinSetupFromUSB来U盘安装windowsXP（不使用win PE系统）(0)
- source insight粘贴时候的光标问题(0)
- 在模块内对内存的分配过早优化的缺点(0)

推荐文章

最新评论

- 使用SIGALRM信号量和sleep冲突的解决办法

  littlefang: @techon:我也发现了，，在有些内核版本里还是不行。。

- winpcap/wireshark在win7 64位系统下IP头部结构长度为0的bug

  littlefang: @orientlight:winpcap64位的版本可以安装，其实这个现象倒也说不上bug，现在网卡...

- Linux动态链接库同名符号装载问题（一）

  yangyangye: 好文章

- 使用SIGALRM信号量和sleep冲突的解决办法

  techon: 例2没问题，可能是例1都在一个线程里，例2是独立线程？

- 使用SIGALRM信号量和sleep冲突的解决办法

  techon: 照样打断sleep啊。。。 我在main的while(1)里加入sleep(10);

- winpcap/wireshark在win7 64位系统下IP头部结构长度为0的bug

  orientlight: 64位的win7也能安装WinPcap么？会不会有什么bug?

- linux中下载ftp文件的几个方法

  littlefang: @lixx870715:首先建立下载文件的url列表，例如url.txt:ftp://ftp.ncb...

- linux中下载ftp文件的几个方法

  lixx870715: 有没有一次用一条命令下载几个文件而不是全部的啊。。

- tcprewrite的安装和用法

  humaeks123: P.S.修改目的IP的MAC地址的时候，MAC地址不是目的机网卡物理地址，而是源机器所连接的交换机M...

  公司简介|招贤纳士|广告服务|银行汇款帐号|联系方式|版权声明|法律顾问|问题报告
  京 ICP 证 070598 号
  北京创新乐知信息技术有限公司 版权所有
  世纪乐知(北京)网络技术有限公司 提供技术支持
  江苏乐知网络技术有限公司 提供商务支持
  联系邮箱：webmaster(at)csdn.net
  Copyright © 1999-2012, CSDN.NET, All Rights Reserved