第一篇 对‘timer_create’未定义的引用

在编译过程中大家可能会报错,大概率的错误提示就是对‘timer_create’未定义的引用

编译的时候注意以下三点:(网友互助回复中高频提到的三点)

1.头文件

#include <signal.h>
#include <time.h>

2. 链接 使用 -lrt

大概 是如下的使用格式

g++ -o xxx xxx.cpp -lrt

如果你跑 这个timer_creater的demo,可参考如下进行编译:

g++ -o timer_creater -lpthread timer_creater.cpp -lrt

特别需要注意的: -lrt需要放到最后面,如果你放到中间,依然会报错!

本篇 第一章节 结束!,下面的3 4 5 是我自己的笔记!


3._POSIX_C_SOURCE 值设置

其实这个担心,可能是被手册给误导了,手册只是提示你这个值必须大于多少;并不是说你的一定不满足,大概率下,你是不用担心的

_POSIX_C_SOURCE >= 199309L

其中:网上讨论最多的是第三条,但是大部分的问题,大概率是第二条 -lrt位置的问题;

4.为何这三条频率会如此之高?

直到我看到了linux API 的帮助后才明白;

man timer_create

原来,这手册中明确提到了 这三点;

uncompress未定义的引用 对未定义的引用_uncompress未定义的引用

_POSIX_C_SOURCE 值怎么判断是否满足199309L:

手册参考文章:https://www.onitroad.com/jc/linux/man-pages/linux/man7/feature_test_macros.7.html 这个文章有详细介绍; 文章有点长,直接运行里面的demo,就可返回_POSIX_C_SOURCE的大小;

_POSIX_C_SOURCE 这个值和glibc有关系
可以用getconf GNU_LIBC_VERSION查看glibc的版本;

hann@hann-virtual-machine:$ getconf GNU_LIBC_VERSION
glibc 2.27

我的版本是2.27 ,好像有这么句话,当然也是有前提的;
700 <= _XOPEN_SOURCE(since glibc 2.10)
_POSIX_C_SOURCE定义为值200809L。

你也可以用上述链接的demo跑一下:
直接运行里面的demo,就可返回_POSIX_C_SOURCE的大小;

如果你不放心:
可以自定义下:
#ifndef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 200809L
#endif

第二篇 使用Linux实现一个高精度定时器

由timer_create()创建的计时器通常称为" POSIX(间隔)计时器"。 POSIX计时器API由以下接口组成:

timer_create():创建一个计时器。(创建定时器)、
timer_settime(2):布防(启动)或撤防(停止)计时器。(初始化定时器)、
timer_gettime(2):获取计时器的下一次到期之前剩余的时间以及计时器的间隔设置。
timer_getoverrun(2):返回上一次计时器到期的溢出计数。 timer_delete(2):撤防并删除计时器。(销毁它)。

创建一个定时器:

介绍参见:https://www.onitroad.com/jc/linux/man-pages/linux/man2/timer_create.2.html

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

进程可以通过调用timer_create()创建特定的定时器,定时器是每个进程自己的,不是在fork时继承的。
timer_create()创建一个新的每个进程间隔计时器。新计时器的ID在timerid指向的缓冲区中返回,该缓冲区必须是非空指针。该ID在该过程中是唯一的,直到删除计时器为止。新计时器最初被撤防;
clockid参数指定新计时器用来测量时间的时钟。可以将其指定为以下值之一:

CLOCK_REALTIME 可设置的系统范围的实时时钟。

sevp.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作为为一个参数。

*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。

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
}

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

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

可以实现1s的定时器,线程回调函数为handle

/*gcc -o pthread_self -lpthread pthread_self.c -lrt*/
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#include <time.h>
#include <pthread.h>
#include <string.h>
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 = 1; //作为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);
}

间隔定时器itimer
系统为每个进程提供了三个间隔定时器。当其中任意一个定时器到期时,就会发出一个信号给进程,同时,定时器重新开始运作。三种定时器描述如下:
ITIMER_REAL 真实时钟,到期时送出SIGALRM信号。
ITIMER_VIRTUAL 仅在进程运行时的计时,到期时送出SIGVTALRM信号。
ITIMER_PROF 不仅在进程运行时计时,在系统为进程运作而运行时它也计时,与ITIMER_VIRTUAL对比,该定时器通常为那些在用户态和核心态空间运行的应用所花去的时间计时,到期时送出SIGPROF信号。
与itimer有关的数据结构定义如下:
这篇文章介绍高精度时间获取,ns us的时间

struct timespec{   
	long tv_sec;     /*   seconds   */   
	long tv_nsec;    /*   nanoseconds   */   
};   
struct timeval{   
    int tv_sec;      /*   seconds   */   
    int tv_usec;     /*   microseconds   */   
};   
struct itimerspec{   
    struct timespec   it_interval;  /*   timer   period   */   
    struct timespec   it_value;     /*   timer   expiration   */   
};   
struct itimerval{   
	struct timeval   it_interval;   /*   timer   interval   */   
	struct timeval   it_value;      /*   current   value   */   
};

创建 带删除的

#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#include <time.h>
#include <pthread.h>
#include <string.h> 

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;
}
void start_timer(int val)
{
    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()的参数
     if(val == 1)
     {
		 ret = timer_create(CLOCK_REALTIME, &evp, &timer);
    if( ret)
        perror("timer_create");
	printf("creater ok. \n"  );
    ts.it_interval.tv_sec = 1;
    ts.it_interval.tv_nsec = 0;
    ts.it_value.tv_sec = 1;
    ts.it_value.tv_nsec = 0;
 
    ret = timer_settime(timer, TIMER_ABSTIME, &ts, NULL);
    if( ret )
        perror("timer_settime");
	 }
	 if(val == 2)
     {
		    if(&timer != NULL)
		   {
			     int ret = timer_delete(timer);
            if(ret < 0)
            {
                printf("PCR timer deletion failed or timer does exist. \n"  );
                return;
            }
			 printf("delete. \n"  );
			 printf("delete. \n"  );
		   }
	 }
 
    
}
 
int main()
{		

		start_timer(1);
		sleep(3);
		start_timer(2);
		sleep(3);

 
    while(1);
	{
 
	}
}

if(bStartDel) // 开启定时器
{
// apl_timer_regist(&m_stTimer);

struct sigevent evp;
    struct itimerspec ts;
    int ret;

// // 初始化为0,否则可能引发段错误
memset(&evp, 0, sizeof(evp));
// evp.sigev_value.sival_ptr = &timer;
evp.sigev_notify = SIGEV_THREAD;
evp.sigev_notify_function = RoutineTick;
evp.sigev_value.sival_int = 3;

ret = timer_create(CLOCK_MONOTONIC, &evp, &timer);
    if(ret < 0)
    {
        apl_error("PCR timer creation failed.\n");
        return;
    }

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

    ret = timer_settime(timer, TIMER_ABSTIME, &ts, NULL);
    if(ret < 0)
    {
        apl_error("PCR timer start failed.\n");
        return;
    }
}
else // 关闭定时器
{
    CPCR::GetIns()->m_nTick = 0;

// apl_timer_unregist(APL_TIMER_HANDLE_S, RoutineTick);

if(timer != NULL)
    {
        int ret = timer_delete(timer);
        if(ret < 0)
        {
            apl_error("PCR timer deletion failed or timer does exist.errno=[%d]\n", errno);
            return;
        }
    }
}