POSIX定时器

最强大的定时器接口来自POSIX时钟系列,这个支持查询、信号和回调函数方式。

创建一个定时器

int timer_create(clockid_t clock_id, struct sigevent *evp, timer_t *timerid)
进程可以通过调用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.

struct sigevent
{
        int sigev_notify; //notification type
        int sigev_signo; //signal number
        union sigval   sigev_value; //signal value
        void (*sigev_notify_function)(union sigval);
        pthread_attr_t *sigev_notify_attributes;
}
union sigval
{
        int sival_int; //integer value
        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);
struct itimespec{
        struct timespec it_interval;
        struct timespec it_value;
};

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

struct timespec{
        time_t tv_sec;
        long tv_nsec;  
};

timer定时的时间间隔,第一次是ts.it_value这么长,后面每次时间间隔是ts.it_interval这么长

    ts.it_interval.tv_sec = 0;

    ts.it_interval.tv_nsec = 200000000; //200ms 

    ts.it_value.tv_sec = 0;

    ts.it_value.tv_nsec = 200000000; //200ms 

    ts.it_interval.tv_sec = 0;

    ts.it_interval.tv_nsec = 200000000; //200ms 

    ts.it_value.tv_sec = 0;

    ts.it_value.tv_nsec = 200000000; //200ms 

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

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不是一个有效的定时器。

下面提供一个信号的例子

timer.c

#include 
#include 
#include 
#include 
#include 

#define CLOCKID CLOCK_REALTIME
#define SIG SIGRTMIN

#define errExit(msg)    do { perror(msg); exit(EXIT_FAILURE); \
					   } while (0)

static void
print_siginfo(siginfo_t *si)
{
   timer_t *tidp;
   int or;

   tidp = si->si_value.sival_ptr;

   printf("    sival_ptr = %p; ", si->si_value.sival_ptr);
   printf("    *sival_ptr = 0x%lx\n", (long) *tidp);

   or = timer_getoverrun(*tidp);
   if (or == -1)
	   errExit("timer_getoverrun");
   else
	   printf("    overrun count = %d\n", or);
}

static void
handler(int sig, siginfo_t *si, void *uc)
{
   /* Note: calling printf() from a signal handler is not
	  strictly correct, since printf() is not async-signal-safe;
	  see signal(7) */

   printf("Caught signal %d\n", sig);
   print_siginfo(si);
   signal(sig, SIG_IGN);
}

int
main(int argc, char *argv[])
{
   timer_t timerid;
   struct sigevent sev;
   struct itimerspec its;
   long long freq_nanosecs;
   sigset_t mask;
   struct sigaction sa;

   if (argc != 3) {
	   fprintf(stderr, "Usage: %s  \n",
			   argv[0]);
	   exit(EXIT_FAILURE);
   }

   /* Establish handler for timer signal */

   printf("Establishing handler for signal %d\n", SIG);
   sa.sa_flags = SA_SIGINFO;
   sa.sa_sigaction = handler;
   sigemptyset(&sa.sa_mask);
   if (sigaction(SIG, &sa, NULL) == -1)
	   errExit("sigaction");

   /* Block timer signal temporarily */

   printf("Blocking signal %d\n", SIG);
   sigemptyset(&mask);
   sigaddset(&mask, SIG);
   if (sigprocmask(SIG_SETMASK, &mask, NULL) == -1)
	   errExit("sigprocmask");

   /* Create the timer */

   sev.sigev_notify = SIGEV_SIGNAL;
   sev.sigev_signo = SIG;
   sev.sigev_value.sival_ptr = &timerid;
   if (timer_create(CLOCKID, &sev, &timerid) == -1)
	   errExit("timer_create");

   printf("timer ID is 0x%lx\n", (long) timerid);

   /* Start the timer */

   freq_nanosecs = atoll(argv[2]);
   its.it_value.tv_sec = freq_nanosecs / 1000000000;
   its.it_value.tv_nsec = freq_nanosecs % 1000000000;
   its.it_interval.tv_sec = its.it_value.tv_sec;
   its.it_interval.tv_nsec = its.it_value.tv_nsec;

   if (timer_settime(timerid, 0, &its, NULL) == -1)
		errExit("timer_settime");

   /* Sleep for a while; meanwhile, the timer may expire
	  multiple times */

   printf("Sleeping for %d seconds\n", atoi(argv[1]));
   sleep(atoi(argv[1]));

   /* Unlock the timer signal, so that timer notification
	  can be delivered */

   printf("Unblocking signal %d\n", SIG);
   if (sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1)
	   errExit("sigprocmask");

   exit(EXIT_SUCCESS);
}

执行gcc timer.c -o timer -lrt

在可执行中,第二个参数为进程sleep时间,单位为s,第三个参数为定时器时间,单位为ns。

输入

rt@ubuntu:~/timerPOSIX$ ./timer 5 1000000000
Establishing handler for signal 34
Blocking signal 34
timer ID is 0x17f9570
Sleeping for 5 seconds
Unblocking signal 34
Caught signal 34
    sival_ptr = 0xbfd69274;     *sival_ptr = 0x17f9570
    overrun count = 4

下面介绍定时启动线程的方法

下面先介绍一下线程的基础

线程属性pthread_attr_t简介

  Posix线程中的线程属性pthread_attr_t主要包括scope属性、detach属性、堆栈地址、堆栈大小、优先级。在pthread_create中,把第二个参数设置为NULL的话,将采用默认的属性配置。
pthread_attr_t的主要属性的意义如下:
__detachstate,表示新线程是否与进程中其他线程脱离同步, 如果设置为PTHREAD_CREATE_DETACHED 则新线程不能用pthread_join()来同步,且在退出时自行释放所占用的资源。缺省为PTHREAD_CREATE_JOINABLE状态。这个属性也可以在线程创建并运行以后用pthread_detach()来设置,而一旦设置为PTHREAD_CREATE_DETACH状态(不论是创建时设置还是运行时设置)则不能再恢复到PTHREAD_CREATE_JOINABLE状态。
__schedpolicy,表示新线程的调度策略,主要包括SCHED_OTHER(正常、非实时)、SCHED_RR(实时、轮转法)和SCHED_FIFO(实时、先入先出)三种,缺省为SCHED_OTHER,后两种调度策略仅对超级用户有效。运行时可以用过pthread_setschedparam()来改变。
__schedparam,一个struct sched_param结构,目前仅有一个sched_priority整型变量表示线程的运行优先级。这个参数仅当调度策略为实时(即SCHED_RR或SCHED_FIFO)时才有效,并可以在运行时通过pthread_setschedparam()函数来改变,缺省为0。
__inheritsched,有两种值可供选择:PTHREAD_EXPLICIT_SCHED和PTHREAD_INHERIT_SCHED,前者表示新线程使用显式指定调度策略和调度参数(即attr中的值),而后者表示继承调用者线程的值。缺省为PTHREAD_EXPLICIT_SCHED。
__scope,表示线程间竞争CPU的范围,也就是说线程优先级的有效范围。POSIX的标准中定义了两个值:PTHREAD_SCOPE_SYSTEM和PTHREAD_SCOPE_PROCESS,前者表示与系统中所有线程一起竞争CPU时间,后者表示仅与同进程中的线程竞争CPU。目前LinuxThreads仅实现了PTHREAD_SCOPE_SYSTEM一值。
  为了设置这些属性,POSIX定义了一系列属性设置函数,包括pthread_attr_init()、pthread_attr_destroy()和与各个属性相关的pthread_attr_getXXX/pthread_attr_setXXX函数。
在设置线程属性 pthread_attr_t 之前,通常先调用pthread_attr_init来初始化,之后来调用相应的属性设置函数。
主要的函数如下:

1、pthread_attr_init
功能:        对线程属性变量的初始化。
头文件:    
函数原型:   int pthread_attr_init (pthread_attr_t* attr);
函数传入值:attr:线程属性。
函数返回值:成功: 0
                失败: -1
2、pthread_attr_setscope
功能:        设置线程 __scope 属性。scope属性表示线程间竞争CPU的范围,也就是说线程优先级的有效范围。POSIX的标准中定义了两个值:PTHREAD_SCOPE_SYSTEM和PTHREAD_SCOPE_PROCESS,前者表示与系统中所有线程一起竞争CPU时间,后者表示仅与同进程中的线程竞争CPU。默认为PTHREAD_SCOPE_PROCESS。目前LinuxThreads仅实现了PTHREAD_SCOPE_SYSTEM一值。
头文件:    
函数原型:   int pthread_attr_setscope (pthread_attr_t* attr, int scope);
函数传入值:attr: 线程属性。
                      scope:PTHREAD_SCOPE_SYSTEM,表示与系统中所有线程一起竞争CPU时间,
                                 PTHREAD_SCOPE_PROCESS,表示仅与同进程中的线程竞争CPU
函数返回值得:同1。
3、pthread_attr_setdetachstate
功能:        设置线程detachstate属性。该表示新线程是否与进程中其他线程脱离同步,如果设置为PTHREAD_CREATE_DETACHED则新线程不能用pthread_join()来同步,且在退出时自行释放所占用的资源。缺省为PTHREAD_CREATE_JOINABLE状态。这个属性也可以在线程创建并运行以后用pthread_detach()来设置,而一旦设置为PTHREAD_CREATE_DETACH状态(不论是创建时设置还是运行时设置)则不能再恢复到PTHREAD_CREATE_JOINABLE状态。
头文件:      
函数原型:    int pthread_attr_setdetachstate (pthread_attr_t* attr, int detachstate);
函数传入值:attr:线程属性。
detachstate:PTHREAD_CREATE_DETACHED,不能用pthread_join()来同步,且在退出时自行释放所占用的资源
                    PTHREAD_CREATE_JOINABLE,能用pthread_join()来同步
函数返回值得:同1。
4、pthread_attr_setschedparam
功能:       设置线程schedparam属性,即调用的优先级。
头文件:    
函数原型:   int pthread_attr_setschedparam (pthread_attr_t* attr, struct sched_param* param);
函数传入值:attr:线程属性。
                 param:线程优先级。一个struct sched_param结构,目前仅有一个sched_priority整型变量表示线程的运行优先级。这个参数仅当调度策略为实时(即SCHED_RR或SCHED_FIFO)时才有效,并可以在运行时通过pthread_setschedparam()函数来改变,缺省为0
函数返回值:同1。
5、pthread_attr_getschedparam
功能:       得到线程优先级。
头文件:    
函数原型:  int pthread_attr_getschedparam (pthread_attr_t* attr, struct sched_param* param);
函数传入值:attr:线程属性;
                    param:线程优先级;
函数返回值:同1。

启动线程定时例子

timerpthread.c

#include 
#include 
#include 
#include 
#include 
#include 

#define CLOCKID CLOCK_REALTIME

#define errExit(msg)    do { perror(msg); exit(EXIT_FAILURE); \
					   } while (0)

/*static*/void fade_in_callback(union sigval v)
{
    timer_t *ptr = (timer_t*)v.sival_ptr;

    //timer_delete(*ptr);//如果删除,会反复进入
	printf("fade_in_callback %d!\n", *ptr);
}

int
main(int argc, char *argv[])
{
	timer_t timerid;
	struct sigevent sev;
	struct itimerspec its;
	long long freq_nanosecs;
	sigset_t mask;
	pthread_attr_t attr = {0};

	if (argc != 3) {
		fprintf(stderr, "Usage: %s  \n",
		        argv[0]);
		exit(EXIT_FAILURE);
	}

	/*初始化属性线程属性*/
	pthread_attr_init (&attr);
	pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM);
	pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);

	/* Create the timer */

	sev.sigev_notify = SIGEV_THREAD;
	sev.sigev_notify_function = fade_in_callback;
	sev.sigev_value.sival_ptr = &timerid;
	if (timer_create(CLOCKID, &sev, &timerid) == -1)
		errExit("timer_create");

	printf("timer ID is 0x%lx\n", (long) timerid);

	/* Start the timer */

	freq_nanosecs = atoll(argv[2]);
	its.it_value.tv_sec = freq_nanosecs / 1000000000;
	its.it_value.tv_nsec = freq_nanosecs % 1000000000;
	its.it_interval.tv_sec = its.it_value.tv_sec;
	its.it_interval.tv_nsec = its.it_value.tv_nsec;

	if (timer_settime(timerid, 0, &its, NULL) == -1)
		errExit("timer_settime");

	/* Sleep for a while; meanwhile, the timer may expire
	  multiple times */

	printf("Sleeping for %d seconds\n", atoi(argv[1]));
	sleep(atoi(argv[1]));

	printf("main out\n");

	exit(EXIT_SUCCESS);
}

gcc timerpthread.c -o timerpthread -lrt -lpthread

./timerpthread 5 1000000000
timer ID is 0x16a9130
Sleeping for 5 seconds
fade_in_callback 23761200!
fade_in_callback 23761200!
fade_in_callback 23761200!
fade_in_callback 23761200!
main out

参考博文

https://blog.csdn.net/a1232345/article/details/45307723

https://blog.csdn.net/hudashi/article/details/7709413

你可能感兴趣的:(编程语言技巧)