内核态常规实现精准定时器再linux2.6版本提出hrtime模块能达到ns级别精准定时
实现方法如下(内核hrtime为我们提供了创建与运行接口):
#include
#include
#include
#include
#include
#include
static struct hrtimer timer;
ktime_t kt;
struct timespec oldtc;
static enum hrtimer_restart hrtimer_hander(struct hrtimer *timer)
{
struct timespec tc;
// printk("I am in hrtimer hander : %lu... \r\n",jiffies);
getnstimeofday(&tc); //获取新的当前系统时间
printk("interval: %ld - %ld = %ld us\r\n",tc.tv_nsec/1000,oldtc.tv_nsec/1000,tc.tv_nsec/1000-oldtc.tv_nsec/1000);
oldtc = tc;
hrtimer_forward(timer,timer->base->get_time(),kt);
return HRTIMER_RESTART;
}
static int __init test_init(void)
{
printk("---------test start-----------\r\n");
getnstimeofday(&oldtc); //获取当前系统时间
kt = ktime_set(0,1000000);//1ms
hrtimer_init(&timer,CLOCK_MONOTONIC,HRTIMER_MODE_REL);
hrtimer_start(&timer,kt,HRTIMER_MODE_REL);
timer.function = hrtimer_hander;
return 0;
}
static void __exit test_exit(void)
{
hrtimer_cancel(&timer);
printk("------------test over---------------\r\n");
}
module_init(test_init);
module_exit(test_exit);
MODULE_LICENSE("GPL");
通过测试发现设置1ms定时,最后延时如下 :
root@ucalgo2:~/timer_pro# cat /proc/kmsg
<4>[ 769.824508] 001: interval: 668501 - 667502 = 999 us
<4>[ 769.825508] 001: interval: 669502 - 668501 = 1001 us
<4>[ 769.826509] 001: interval: 670502 - 669502 = 1000 us
<4>[ 769.827509] 001: interval: 671502 - 670502 = 1000 us
<4>[ 769.828509] 001: interval: 672502 - 671502 = 1000 us
<4>[ 769.829508] 001: interval: 673501 - 672502 = 999 us
<4>[ 769.830509] 001: interval: 674502 - 673501 = 1001 us
<4>[ 769.831508] 001: interval: 675501 - 674502 = 999 us
<4>[ 769.832508] 001: interval: 676501 - 675501 = 1000 us
<4>[ 769.833508] 001: interval: 677501 - 676501 = 1000 us
<4>[ 769.835051] 001: interval: 679044 - 677501 = 1543 us
<4>[ 769.836112] 001: interval: 680105 - 679044 = 1061 us
<4>[ 769.836509] 001: interval: 680502 - 680105 = 397 us
<4>[ 769.837509] 001: interval: 681502 - 680502 = 1000 us
<4>[ 769.838508] 001: interval: 682502 - 681502 = 1000 us
<4>[ 769.839508] 001: interval: 683501 - 682502 = 999 us
<4>[ 769.840508] 001: interval: 684501 - 683501 = 1000 us
<4>[ 769.841509] 001: interval: 685502 - 684501 = 1001 us
<4>[ 769.842508] 001: interval: 686502 - 685502 = 1000 us
<4>[ 769.845509] 001: interval: 689502 - 688503 = 999 us
测试设置10us定时器,最后延时如下:
cat /proc/kmsg
<4>[ 1139.536530] 000: interval: 380539 - 380451 = 88 us
<4>[ 1139.536542] 000: interval: 380551 - 380539 = 12 us
<4>[ 1139.536552] 000: interval: 380560 - 380551 = 9 us
<4>[ 1139.536561] 000: interval: 380570 - 380560 = 10 us
<4>[ 1139.536572] 000: interval: 380581 - 380570 = 11 us
<4>[ 1139.536582] 000: interval: 380590 - 380581 = 9 us
<4>[ 1139.536592] 000: interval: 380601 - 380590 = 11 us
<4>[ 1139.536602] 000: interval: 380610 - 380601 = 9 us
<4>[ 1139.536612] 000: interval: 380621 - 380610 = 11 us
<4>[ 1139.536622] 000: interval: 380631 - 380621 = 10 us
<4>[ 1139.536632] 000: interval: 380641 - 380631 = 10 us
<4>[ 1139.536642] 000: interval: 380651 - 380641 = 10 us
<4>[ 1139.536652] 000: interval: 380660 - 380651 = 9 us
<4>[ 1139.536662] 000: interval: 380671 - 380660 = 11 us
<4>[ 1139.536672] 000: interval: 380680 - 380671 = 9 us
方法一:注册时钟信号到内核hrtimer模块,时间到达触发调度,具体代码如下:
#include
#include
struct timeval old_time,new_time;
unsigned int val;
//事件1
void timeout1(union sigval sig)
{
gettimeofday(&new_time, NULL);
val = new_time.tv_sec*1000000 + new_time.tv_usec - (old_time.tv_sec*1000000 + old_time.tv_usec);
printf("itvl:%d\n", val);
old_time = new_time;
}
void create_timer_by_signal(void)
{
static timer_t times;
struct itimerspec ts; //用于配置定时器时间
struct sigevent evp;
evp.sigev_notify = SIGEV_THREAD;
evp.sigev_notify_function = timeout1;
evp.sigev_value.sival_int = 2;
evp.sigev_notify_attributes = NULL;
timer_create(CLOCK_REALTIME, &evp, ×);
/*第一次调用后,每次调用间隔时间*/
ts.it_interval.tv_sec = 0;
ts.it_interval.tv_nsec = 0; // 1000ns = 1us 1000us = 1ms 1000ms = 1s
/*第一次调用时间*/
ts.it_value.tv_sec = 0;
ts.it_value.tv_nsec = 10000;//10us
//signal(SIGUSR1, timeout1);
gettimeofday(&old_time, NULL);
timer_settime(times, 0, &ts, NULL);
}
int main()
{
int ret;
create_timer_by_signal();
while(1){
usleep(300000);
}
}
方法二:通过休眠的方式解决定时效果:
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include
#include
#include
#include
#include
#include
#include
#include
struct timeval te;
unsigned int my_gettime()
{
gettimeofday(&te, NULL);
return (te.tv_sec * 1000000 + te.tv_usec);
}
int ble_npl_task_cpu_bind(int cpu,int thread_id)
{
cpu_set_t mask; //CPU˵ļ
CPU_ZERO(&mask); //ÿ
CPU_SET(cpu, &mask); //ֵ
if (sched_setaffinity(thread_id, sizeof(mask), &mask) == -1)//߳CPU
{
return 1;
}
return 0;
}
void *testfunc(void *argv)
{
struct timespec t;
int i = 20;
struct timeval tv, tv1, tv2;
ble_npl_task_cpu_bind(2, 0);
printf("rtc timer bind of cpu:%d\n", 2); //绑定cpu
t.tv_sec = 0;
t.tv_nsec = 27520;//32khz
//TIMER_ABSTIME
while(i--){
gettimeofday(&tv, NULL);
clock_nanosleep(CLOCK_MONOTONIC, 0, &t, NULL);
gettimeofday(&tv1, NULL);
printf("%lu\n", (tv1.tv_sec *1000000 + tv1.tv_usec) -
(tv.tv_sec *1000000 + tv.tv_usec));
}
}
int main()
{
int i = 10, j = 1000000, z = 100;
unsigned long long k = 10000000000;
unsigned long itvl[100], itvl1[100], tmp = 0, err = 0, test2 = 1;
struct timeval tv, tv1, tv2;
struct timeval test[100], test1[100];
struct sched_param param;
param.sched_priority = 90;
sched_get_priority_max(SCHED_FIFO);
sched_setscheduler(getpid(),SCHED_FIFO, ¶m);
//测试在线程中定时器功能
#if 0
{
pthread_attr_t attr;
pthread_t handle;
struct sched_param param;
err = pthread_attr_init(&attr);
if (err) return err;
err = pthread_attr_getschedparam (&attr, ¶m);
if (err) return err;
err = pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
if (err) return err;
param.sched_priority = 90;
err = pthread_attr_setschedparam (&attr, ¶m);
if (err) return err;
// err = pthread_create(&handle, &attr, testfunc, NULL);
// printf("err4:%d", err);
}
#endif
//实现150us定时器
struct timespec t;
t.tv_sec = 0;
t.tv_nsec = 147000;//150us
//TIMER_ABSTIME
while(i--){
gettimeofday(&tv, NULL);
clock_nanosleep(CLOCK_MONOTONIC, 0, &t, NULL);
gettimeofday(&tv1, NULL);
printf("%lu\n", (tv1.tv_sec *1000000 + tv1.tv_usec) -
(tv.tv_sec *1000000 + tv.tv_usec));
}
i = 100;
//实现32khz定时器
t.tv_sec = 0;
t.tv_nsec = 20520;//32khz
while(i--){
itvl[i] = my_gettime();
clock_nanosleep(CLOCK_MONOTONIC, 0, &t, NULL);
itvl1[i] = my_gettime();
}
for (i =0; i< 100;i++) {
printf("%lu\n", itvl1[i] - itvl[i]);
}
return 0;
}
以上所有实现需要满足:1)将linux打入实时补丁;
2)设置CPU隔离,并将进程运行在隔离CPU上
实时补丁如文章:
Unbuntu16.04 安装实时内核之RT-PREEMP补丁_ubuntu实时补丁_w_melody的博客-CSDN博客
CPU隔离如文章:
linux CPU隔离_w_melody的博客-CSDN博客