博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

定时器 间隔定时器 信号

Posted on 2016-03-23 11:30  bw_0927  阅读(363)  评论(0)    收藏  举报

http://www.zyfforlinux.cc/2014/10/20/%E4%BF%A1%E5%8F%B7%E6%93%8D%E4%BD%9C%E8%AF%A6%E8%A7%A3-%E4%B8%8A/

 

信号的应用

  • 结合定时器和间隔计时器实现一些多任务
    下面先简单的介绍下定时器和间隔计时器。
    • 定时器:
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
非阻塞函数,定时时间到了就发送SIGALRM信号,如果在alarm之前已经调用了alarm并且时间没有到的话,那么此次返回之前alarm剩余的时间,否则返回0
 
#include <sys/time.h>
int getitimer(int which, struct itimerval *curr_value);
int setitimer(int which, //计时方式 可以选择 ITIMER_REAL/ITIMER_VIRTUAL/ITIMER_PROF
const struct itimerval *new_value,//时间参数
struct itimerval *old_value //上一次设置的定时器
);

struct itimerval {
struct timeval it_interval; /* next value */ 间隔时间
struct timeval it_value; /* current value */ 延迟时间 //延时it_value后,第一次触发SIGALRM信号,之后每隔it_iterval时间后触发一次
};

struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
};
间隔计数器,首先在it_value秒后会产生SIGALRM信号,以后每隔it_interval秒后都会产生SIGALARM信号
 


- 结合间隔计数器实现多任务案例:
- 间隔计时器:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<curses.h>
#include<time.h>
#include <sys/time.h>
#include<signal.h>
void end();
void init();
void task1();
void task2(int signum);

int main()
{
struct itimerval itm;
//间隔时间
itm.it_interval.tv_sec=2;
itm.it_interval.tv_usec=0;
//延迟时间
itm.it_value.tv_sec = 5;
itm.it_value.tv_usec =0;
init();
signal(SIGALRM,task2);
//加入间隔计数器执行任务二
setitimer(ITIMER_REAL,&itm,NULL);
//执行任务一
task1();


}
void init()
{
initscr();
box(stdscr,0,0);
noecho();

}

void task1()
{
time_t tm;
char *tmstr;
while(1){
time(&tm);
tmstr = ctime(&tm);
mvprintw(10,20,"%s",tmstr);
refresh();
}
}

void task2(int signum)
{
int num=rand()%100;
mvprintw(5,15,"%d",num);
refresh();
}

void end()
{
endwin();
}

上面是一个利用信号+间隔计时器实现的一个多任务,主任务显示不停的显示时间,第二个任务,则是每隔几秒显示一个随机数,这两个任务很好的共存。
  • 进程间通信    传递数据
struct sigaction
{
void (*sa_handle)(int); //信号处理函数的地址,或者是SIG_IGN SIG_DFL
void (*sa_sigaction)(int,siginfo_t*,void*);
sigset_t sa_mask; //要屏蔽的信号集
int sa_flags;//SA_SIGINFO
void (*sa_restorer)(void); //保留成员
}
其中void (*sa_handle)(int) 和 void (*sa_sigaction)(int,siginfo_t*,void*); 只选其中之一取决于flags,如果sa_flasg=0那么就是前者,相当于普通的信号处理函数
如果sa_flasg=SA_SIGINFO 那么就是后者.
siginfo_t {
int si_signo; /* Signal number */
int si_errno; /* An errno value */
int si_code; /* Signal code */
int si_trapno; /* Trap number that caused
hardware-generated signal
(unused on most architectures) */
pid_t si_pid; /* Sending process ID */
uid_t si_uid; /* Real user ID of sending process */
int si_status; /* Exit value or signal */
clock_t si_utime; /* User time consumed */
clock_t si_stime; /* System time consumed */
sigval_t si_value; /* Signal value */
int si_int; /* POSIX.1b signal */
void *si_ptr; /* POSIX.1b signal */
int si_overrun; /* Timer overrun count; POSIX.1b timers */
int si_timerid; /* Timer ID; POSIX.1b timers */
void *si_addr; /* Memory location which caused fault */
int si_band; /* Band event */
int si_fd; /* File descriptor */
}


#include <sys/types.h>
#include <signal.h>
int sigqueue(pid_t pid, int sig, const union sigval value);
union sigval {
int sival_int; //可以传递两种类型的数据,整型或一个指针
void *sival_ptr;
};

使用示列:
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void handle(int s,siginfo_t* info,void *d)
{
printf("接受到的数据:%d\n",info->si_int);
}

main()
{
struct sigaction act={0};
act.sa_sigaction=handle;
sigemptyset(&act.sa_mask);
sigaddset(&act.sa_mask,SIGINT);
act.sa_flags=SA_SIGINFO;
sigaction(SIGUSR1,&act,0);
printf("pid:%d\n",getpid());
while(1);
}

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
main()
{
union sigval val;
val.sival_int=8888;
sigqueue(pid,SIGUSR1,val); //pid是上面程序输出的pid向上面这个程序发送带有数据的信号
}
程序输出:
[root@localhost apue]# ./a.out
pid:11214
接受到的数据:8888
int raise(int sig);
允许进程向自身发送信号


========================
http://blog.csdn.net/solstice/article/details/6173563

Linux 时间函数

Linux 的计时函数,用于获得当前时间:

  • time(2) / time_t (秒)
  • ftime(3) / struct timeb (毫秒)
  • gettimeofday(2) / struct timeval (微秒)
  • clock_gettime(2) / struct timespec (纳秒)
  • gmtime / localtime / timegm / mktime / strftime / struct tm (这些与当前时间无关)

定时函数,用于让程序等待一段时间或安排计划任务:

  • sleep
  • alarm
  • usleep
  • nanosleep
  • clock_nanosleep
  • getitimer / setitimer
  • timer_create / timer_settime / timer_gettime / timer_delete
  • timerfd_create / timerfd_gettime / timerfd_settime

我的取舍如下:

  • (计时)只使用 gettimeofday 来获取当前时间。
  • (定时)只使用 timerfd_* 系列函数来处理定时。

gettimeofday 入选原因:(这也是 muduo::Timestamp class 的主要设计考虑)

  1. time 的精度太低,ftime 已被废弃,clock_gettime 精度最高,但是它系统调用的开销比 gettimeofday 大。
  2. 在 x86-64 平台上,gettimeofday 不是系统调用,而是在用户态实现的(搜 vsyscall),没有上下文切换和陷入内核的开销。
  3. gettimeofday 的分辨率 (resolution) 是 1 微秒,足以满足日常计时的需要。muduo::Timestamp 用一个 int64_t 来表示从 Epoch 到现在的微秒数,其范围可达上下 30 万年。

timerfd_* 入选的原因:

  1. sleep / alarm / usleep 在实现时有可能用了信号 SIGALRM,在多线程程序中处理信号是个相当麻烦的事情,应当尽量避免。(近期我会写一篇博客仔细讲讲“多线程、RAII、fork() 与信号”)
  2. nanosleep 和 clock_nanosleep 是线程安全的,但是在非阻塞网络编程中,绝对不能用让线程挂起的方式来等待一段时间,程序会失去响应。正确的做法是注册一个时间回调函数
  3. getitimer 和 timer_create 也是用信号来 deliver 超时,在多线程程序中也会有麻烦。timer_create 可以指定信号的接收方是进程还是线程,算是一个进步,不过在信号处理函数(signal handler)能做的事情实在很受限
  4. timerfd_create 把时间变成了一个文件描述符,该“文件”在定时器超时的那一刻变得可读,这样就能很方便地融入到 select/poll 框架中,用统一的方式来处理 IO 事件和超时事件,这也正是 Reactor 模式的长处。我在一年前发表的《Linux 新增系统调用的启示》中也谈到这个想法,现在我把这个想法在 muduo 网络库中实现了。
  5. 传统的 Reactor 利用 select/poll/epoll 的 timeout 来实现定时功能,但 poll 和 epoll 的定时精度只有毫秒,远低于 timerfd_settime 的定时精度

必须要说明,在 Linux 这种非实时多任务操作系统中,在用户态实现完全精确可控的计时和定时是做不到的,因为当前任务可能会被随时切换出去,这在 CPU 负载大的时候尤为明显。但是,我们的程序可以尽量提高时间精度,必要的时候通过控制 CPU 负载来提高时间操作的可靠性,在程序在 99.99% 的时候都是按预期执行的。这或许比换用实时操作系统并重新编写并测试代码要经济一些。

关于时间的精度(accuracy)问题我留到专题博客文章中讨论,它与分辨率(resolution)不完全是一回事儿。时间跳变和闰秒的影响与应对也不在此处展开讨论了。

 

在非阻塞服务端编程中,绝对不能用  sleep 或类似的办法来让程序原地停留等待,这会让程序失去响应,因为主事件循环被挂起了,无法处理 IO 事件。这就像在 Windows 编程中绝对不能在消息循环里执行耗时的代码一样,会让程序界面失去响应。