Linux定时器
1. 睡眠函数
Linux下有两个睡眠函数,
#include <unistd.h>
unsigned int sleep(unsigned int seconds);
int usleep(useconds_t usec);
unsigned int alarm(unsigned int seconds);
1.seconds秒后触发一个SIGALRM信号,具体操作需要在信号处理函数中完成
2.seconds设置为0,则所有正在等待的alarm全部被取消
3.alarm和settimer共享共一个定时器,调用一个会影响到另一个
4.alarm创造的Alarms可以被Execve保留但是不会被fork产生的子进程继承
5.sleep的实现可能会用到SIGALRM,因此混用alarm与sleep会导致错误
2. 时钟处理
+ Linux为每个进程维护3个计时器,分别是,
-
真实计时器 计算程序运行的实际时间 发送SIGALRM 信号
-
虚拟计时器 计算的时程序运行在用户态时所消耗的时间,实际时间减去系统调用和程序睡眠所消耗的时间 发送SIGVTALRAM 信号
-
实用计时器 程序处于用户态和处于内核态所消耗的时间之和 发送SIGPROF信号
用指定的初始间隔和重复间隔时间为进程设定好一个计时器后,该计时器就会定时的向进程发送时钟信号。·
-
函数和数据结构
#include <signal.h> #include <time.h> 链接指令 -lrt int timer_create(clockid_t clockid, struct sigevent *sevp, timer_t *timerid); 创建一个新的进程内部的定时器变量,定时器id使用timerid返回,因此timerfd不能为空指针,该id在进程内唯一。clockid参数可以被设置为以下值: CLOCK_REALTIME CLOCK_MOONOTONIC CLOCK_PROCESS_CPUTIME_ID CLOCK_THREAD_CPUTIME_ID CLOCK_BOOTTIME CLOCK_REALTIME_ALARM CLOCK_BOOTTIME_ALARM #include <sys/time.h> 1.//获取计时器的设置 int getitimer(int which, struct itimerval *curr_value); 2.//设置计时器 int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value); struct itimerval { struct timeval it_interval; /* Interval for periodic timer */ struct timeval it_value; /* Time until next expiration */ }; struct timeval { time_t tv_sec; /* seconds */ suseconds_t tv_usec; /* microseconds */ }; 参数which指定哪个计时器,可选项为 ITIMER_REAL(真实计时器)、 ITIMER_VIRTUAL(虚拟计时器、 ITIMER_PROF(实用计时器)。
3. 利用文件描述符进行通知的定时器:timerfd API
#include <sys/timerfd.h> int timerfd_create(int clockid, int flags); clockid 可以被设置为 CLOCK_REALTIME 相对时间,从1970.1.1到目前的时间。更改系统时间 会更改获取的值,它以系统时间为坐标。 `CLOCK_MONOTONIC`绝对时间,获取的时间为系统重启到现在的时间,更改系统时间对齐没有影响。 参数flags可以被设置为 TFD_NONBLOCK(非阻塞) TFD_CLOEXEC(同`O_CLOEXEC`) int timerfd_settime(int fd, int flags, const struct itimerspec *new_value, struct itimerspec *old_value); int timerfd_gettime(int fd, struct itimerspec *curr_value); struct timespec { time_t tv_sec; /* Seconds */ long tv_nsec; /* Nanoseconds */ }; struct itimerspec { struct timespec it_interval; /* Interval for periodictimer */ struct timespec it_value; /* Initial expiration */ }; 参数fd: timerfd对应的文件描述符 参数flag可被设置为 0 表示相对定时器 TFD_TIMER_ABSTIME 表示是绝对定时器 参数new_value:设置超时时间,如果为0,则表示停止定时器
参数old_value:一般设置为nullptr,如果不设置为nullptr则返回定时器此次之前设置的超时时间
这些系统调用通过文件描述符创建定时器并传递定时器到期通知,提供了对set_timer和timer_create的替代方案。好处是文件描述符可以被select\poll\epoll监控,相对信号控制方便很多。用法与timer的用法类似
- 定时器类的C++封装
接口设计:
- 设定等待间隔和等待时间、开始计时,结束计时,以及计时到期后触发的时间
- 内部维护一个任务队列,定期执行任务队列中的任务。
- 为了让每个任务可以有自己的执行ll时间间隔,可以规定定时器管理类的间隔单位。
- 设置一个任务类,其中设置每个任务的执行间隔,距离下次执行任务剩余的时间间隔。
- 定时器管理类,每隔最小时间间隔单位去检查任务队列中的任务,如果其内部计时器小于等于0,则执行该任务,并将计时器归0.否则,在定时器上减去一个基本单位
- 为了管理任务队列中的数据,可以考虑增加一个待加入队列和待删除队列。
- 在每个基本时间间隔过期时,检查待加入任务队列和待删除任务队列。并将其中的任务加入/删除 任务队列
4. 时间轮盘 ->实现定期执行任务。
- 设置一个循环队列,其长度为定时器管理类的基本时间间隔总数,
- 设置一个指向队尾的指针,这个指针每秒移动一格,
- 链表中的每一个节点本身维护一个任务队列。
- 每次定时器到期后检查该时间节点的槽,如果槽内有任务。则将槽内的任务执行,并结合任务的执行间隔,将任务添加到下一个需要执行的时间节点
- 当前时间槽执行完毕后进行清空操作。继续执行下一个时间槽。