详细剖析Linux和RTOS(RT-Thread)的时钟和定时器的使用

Linux发烧友

  • 1.RTOS篇
    • 1.1RT-Thread简介
    • 1.2时钟管理
      • 1.2.1时钟节拍
    • 1.3获取系统节拍
    • 1.4定时器分类
    • 1.5定时器源码分析
    • 1.6定时器相关函数
    • 1.61动态创建一个定时器和删除定时器
    • 1.7初始化和脱离定时器
    • 1.8启动和停止定时器
    • 1.9高精度延时
    • 1.10实战篇:RTOS定时器代码演示
  • 2Linux篇
    • 2.1Linux简介
    • 2.2Linux定时器机制
    • 2.3alarm类定时器
    • 2.4进程接收到信号后的处理方式
    • 2.5实战篇1:alarm定时器代码演示
    • 2.6setitimer定时器的设置
    • 2.7实战篇2:setitimer的代码演示
    • 2.8信号的简要说明

1.RTOS篇

1.1RT-Thread简介

RT-Thread 是一个集实时操作系统(RTOS)内核、中间件组件和开发者社区于一体的技术平台,由熊谱翔先生带领并集合开源社区力量开发而成,RT-Thread 也是一个组件完整丰富、高度可伸缩、简易开发、超低功耗、高安全性的物联网操作系统。RT-Thread 具备一个 IoT OS 平台所需的所有关键组件,例如GUI、网络协议栈、安全传输、低功耗组件等等。经过11年的累积发展,RT-Thread 已经拥有一个国内最大的嵌入式开源社区,同时被广泛应用于能源、车载、医疗、消费电子等多个行业,累积装机量超过 8亿 台,成为国人自主开发、国内最成熟稳定和装机量最大的开源 RTOS。

1.2时钟管理

1.2.1时钟节拍

任何系统都有一个时钟节拍,负责处理和时间相关的事件,在RT-Thread中时钟节拍可以根据RT_TICK_PER_SECOND宏来定义,下面设定为频率时1000HZ节拍是1ms
详细剖析Linux和RTOS(RT-Thread)的时钟和定时器的使用_第1张图片
和裸机的STM32一样,有一个系统滴答定时器,每1ms触发一次中断
在滴答中断函数里面会有一个全局变量,中断一次就加一

1.3获取系统节拍

我们可以通过下面一个函数获取滴答定时器里面的全局变量。

rt_tick_t rt_tick_get(void)

1.4定时器分类

定时器可以分为硬件定时器和软件定时器

  1. 硬件定时器:从代码的角度上看,硬件定时器更加精确,可以到达纳妙级别,因为他是通过外部晶振提供给芯片提供时钟。芯片提供一组寄存器,到达设定时钟后产生中断
  2. 软件定时器:建立在硬件定时器的基础上,可以提供多个定时器

1.5定时器源码分析

  1. 在RT-Thread启动的时候,会调用rtthread_startup函数,里面初始化了许多事件,如定时器初始化函数rt_system_timer_init();
  2. 可以看到定时器初始化就是遍历rt_timer_list,对每一个rt_timer_list的内容进行 rt_list_init(rt_timer_list + i)操作详细剖析Linux和RTOS(RT-Thread)的时钟和定时器的使用_第2张图片
  3. 那么rt_timer_list是什么呢,其实就是一个队列链表
struct rt_list_node
{
    struct rt_list_node *next;     /**< point to next node. */
    struct rt_list_node *prev;     /**< point to prev node. */
};
typedef struct rt_list_node rt_list_t; 
  1. rt_list_init里面又执行什么操作呢
t_inline void rt_list_init(rt_list_t *l)
{
    l->next = l->prev = l;
}
  1. rt_system_timer_thread_init软件定时器初始化,其实就是把软件定时器放入rt_list_init中,然后给每一个软件定时器定时器开启线程。
void rt_system_timer_thread_init(void)
{
#ifdef RT_USING_TIMER_SOFT
    int i;

    for (i = 0;
         i < sizeof(rt_soft_timer_list) / sizeof(rt_soft_timer_list[0]);
         i++)
    {
        rt_list_init(rt_soft_timer_list + i);
    }

    /* start software timer thread */
    rt_thread_init(&timer_thread,
                   "timer",
                   rt_thread_timer_entry,
                   RT_NULL,
                   &timer_thread_stack[0],
                   sizeof(timer_thread_stack),
                   RT_TIMER_THREAD_PRIO,
                   10);

    /* startup */
    rt_thread_startup(&timer_thread);
#endif
}

1.6定时器相关函数

1.61动态创建一个定时器和删除定时器

添加定时器

/**
 * This function will create a timer
 *
 * @param name the name of timer
 * @param timeout the timeout function
 * @param parameter the parameter of timeout function
 * @param time the tick of timer
 * @param flag the flag of timer
 * #define RT_TIMER_FLAG_ONE_SHOT         0x0             /**< one shot timer */
* #define RT_TIMER_FLAG_PERIODIC           0x2             /**< p	eriodic timer **/
* #define RT_TIMER_FLAG_HARD_TIMER      0x0             /**< hard timer,the timer's callback function will be called in tick isr. */
* #define RT_TIMER_FLAG_SOFT_TIMER       0x4            /**< soft timer,the timer's callback function will be called in timer thread. */
* @return the created timer object
**/
rt_timer_t rt_timer_create(const char *name,
                           void (*timeout)(void *parameter),
                           void       *parameter,
                           rt_tick_t   time,
                           rt_uint8_t  flag)

定时器删除

/**
 * This function will delete a timer and release timer memory
 *
 * @param timer the timer to be deleted
 *
 * @return the operation status, RT_EOK on OK; RT_ERROR on error
 */
rt_err_t rt_timer_delete(rt_timer_t timer)

1.7初始化和脱离定时器

静态初始化

/**
 * This function will initialize a timer, normally this function is used to
 * initialize a static timer object.
 *
 * @param timer the static timer object  (typedef struct rt_timer *rt_timer_t;)
 * @param name the name of timer
 * @param timeout the timeout function
 * @param parameter the parameter of timeout function
 * @param time the tick of timer
 * @param flag the flag of timer
 */
void rt_timer_init(rt_timer_t  timer,
                   const char *name,
                   void (*timeout)(void *parameter),
                   void       *parameter,
                   rt_tick_t   time,
                   rt_uint8_t  flag)

定时器脱离

/**
 * This function will detach a timer from timer management.
 *
 * @param timer the static timer object
 *
 * @return the operation status, RT_EOK on OK; RT_ERROR on error
 */
rt_err_t rt_timer_detach(rt_timer_t timer)

1.8启动和停止定时器

启动定时

**
 * This function will start the timer
 *
 * @param timer the timer to be started
 *
 * @return the operation status, RT_EOK on OK, -RT_ERROR on error
 */
rt_err_t rt_timer_start(rt_timer_t timer)

停止定时器

/**
 * This function will stop the timer
 *
 * @param timer the timer to be stopped
 *
 * @return the operation status, RT_EOK on OK, -RT_ERROR on error
 */
rt_err_t rt_timer_stop(rt_timer_t timer)
``
## 定时器控制
当定时器启动后我们想更改定时器的某些状态,可以调用下面的函数

```c
/**
 * This function will get or set some options of the timer
 *
 * @param timer the timer to be get or set
 * @param cmd the control command
 * @param arg the argument
 * #define RT_TIMER_CTRL_SET_TIME          0x0            /**< set timer control command */
 * #define RT_TIMER_CTRL_GET_TIME          0x1            /**< get timer control command */
 * #define RT_TIMER_CTRL_SET_ONESHOT       0x2            /**< change timer to one shot */
* #define RT_TIMER_CTRL_SET_PERIODIC      0x3             /**< change timer to periodic */
* @return RT_EOK
*/
rt_err_t rt_timer_control(rt_timer_t timer, int cmd, void *arg)

1.9高精度延时

注意:这个函数只支持低于1个OS Tick的延时, 否则SysTick会出现溢出而不能够获得指定的延时时间
/**

  • This function will delay for some us.
  • @param us the delay time of us
    */
    void rt_hw_us_delay(rt_uint32_t us)

1.10实战篇:RTOS定时器代码演示

效果:定时打印结果,当打印十次后调用rt_timer_control(&tm2, RT_TIMER_CTRL_SET_ONESHOT,NULL);变为一次性的定时器。

/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2021-11-13     15118       the first version
 */
#include 

rt_timer_t tm1 ;
struct rt_timer tm2;

int flags = 0;

void tm1_callback(void *parameter)
{
    rt_kprintf("tm1_callback running...\n");
}

void tm2_callback(void *parameter)
{
    flags++;
    if(flags == 10){
        rt_timer_control(&tm2, RT_TIMER_CTRL_SET_ONESHOT,NULL);
        flags = 0;
    }
    rt_tick_t timeout = 1000;
    rt_timer_control(&tm2, RT_TIMER_CTRL_SET_TIME , (void *)&timeout);
    rt_kprintf("[%u]tm2_callback running...\n",rt_tick_get());//获取系统节拍数
}


int main()
{
    //动态创建定时器
    tm1 = rt_timer_create("tm1_demo",tm1_callback, NULL, 3000,  \
                RT_TIMER_FLAG_PERIODIC | RT_TIMER_FLAG_SOFT_TIMER);
    if(tm1 == RT_NULL){
            LOG_E("rt_timer_create faile...\n");
            return -ENOMEM;
        }
        LOG_D("rt_timer_create successed...\n");
    rt_timer_start(tm1);

    //静态定时器创建
    //静态创建定时器
       rt_timer_init(&tm2, "tm2_demo", tm2_callback, NULL, 3000, \
               RT_TIMER_FLAG_PERIODIC | RT_TIMER_FLAG_SOFT_TIMER);
       rt_timer_start(&tm2);
     return 1;
}

2Linux篇

2.1Linux简介

Linux 内核最初只是由芬兰人林纳斯·托瓦兹(Linus Torvalds)在赫尔辛基大学上学时出于个人爱好而编写的。
Linux 是一套免费使用和自由传播的类 Unix 操作系统,是一个基于 POSIX 和 UNIX 的多用户、多任务、支持多线程和多 CPU 的操作系统。
Linux 能运行主要的 UNIX 工具软件、应用程序和网络协议。它支持 32 位和 64 位硬件。Linux 继承了 Unix 以网络为核心的设计思想,是一个性能稳定的多用户网络操作系统。

2.2Linux定时器机制

Linux分有内核定时器和应用层定时器,在这里我们只讨论应用层的定时器

说起Linux的定时器,又不得不说起信号,应用程序会给系统订一个时间,如果这个时间到达之后,就会发送SIGALRM信号,这个默认的动作是终止调用某个进程

2.3alarm类定时器

这个是最简单的定时器,我们只要使用它的时候调用它就行了,不过他会和sleep函数冲突,因为sleep()函数会中断信号,使得定时器失效,定时之后我们可以去写他的信号函数,在里面执行回调函数,在里面做自己想做的事

#include 
unsigned int alarm(unsigned int seconds);
功能:
在 seconds 秒后,向调用进程发送一个 SIGALRM 信号,SIGALRM 信号的默认动作是终止调用 alarm 函数的进程。
返回值:
若以前没有设置过定时器, 或设置的定时器已超时, 返回 0; 否则返回定时器剩余的秒数, 并重新设定定时器。

2.4进程接收到信号后的处理方式

现在我们知道alarm函数是用来接收SIGALRM信号的,当我们这个信号接收到之后会执行它默认的处理方式,现在我们需要重写它默认的处理方式。程序中可用函数 signal()改变信号的处理方式。

#include 
typedef void (*sighandler_t)(int)sighandler_t signal(int signum,sighandler_t handler);
功能:
注册信号处理函数( 不可用于 SIGKILL、 SIGSTOP 信号), 即确定收到信号后处理函数的入口地址。
参数:
signum: 信号编号
handler 的取值:
忽略该信号: SIG_IGN
执行系统默认动作: SIG_DFL
自定义信号处理函数: 信号处理函数名
返回值:
成功: 返回函数地址, 该地址为此信号上一次注册的信号处理函数的地址。
失败: 返回 SIG_ERR

2.5实战篇1:alarm定时器代码演示

效果:五秒后打印魔动山霸你好呀

/* ************************************************************************
 *       Filename:  test.c
 *    Description:  
 *        Version:  1.0
 *        Created:  2021年11月14日 14时37分25秒
 *       Revision:  none
 *       Compiler:  gcc
 *         Author:  YOUR NAME (), 
 *        Company:  
 * ************************************************************************/
#include 
#include 
#include 
void signal_handeler(int signo)
{
	int a=31;
	printf("%c\n",a);
	printf("%d魔动山霸你好呀\n",signo);
}
int main()
{
	int serconds = 0;
	serconds = alarm(5);
	printf("%d\n",serconds);
	printf("seconds %d\n",serconds);
	signal(SIGALRM,signal_handeler);
	while(1);
}

2.6setitimer定时器的设置

函数alarm设置的定时器只能精确到秒,而以下函数理论上可以精确到微妙

int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue);

函数setitimer可以提供三种定时器,它们相互独立,任意一个定时完成都将发送定时信号到进程,并且自动重新计时。参数which确定了定时器的类型,如表10-6所示:表10-6 参数which与定时器类型取值 含义 信号发送 ITIMER_REAL 定时真实时间,与alarm类型相同。 SIGALRM ITIMER_VIRT 定时进程在用户态下的实际执行时间。
SIGVTALRM ITIMER_PROF 定时进程在用户态和核心态下的实际执行时间。 SIGPROF 这三种定时器定时完成时给进程发送的信号各不相同,其中

  • ITIMER_REAL类定时器发送SIGALRM信号
  • ITIMER_VIRT类定时器发送SIGVTALRM信号
  • ITIMER_REAL类定时器发送SIGPROF信号。
    函数alarm本质上设置的是低精确、非重载的ITIMER_REAL类定时器,它只能精确到秒,并且每次设置只能产生一次定时。函数setitimer设置的定时器则不同,它们不但可以计时到微妙(理论上),还能自动循环定时。
在一个Unix进程中,不能同时使用alarm和ITIMER_REAL类定时器。结构itimerval描述了定时器的组成: struct itimerval { 
   struct tim. it_interval;    /* 下次定时取值 */  
   struct tim. it_value;       /* 本次定时设置值 */
}   
 结构tim.描述了一个精确到微妙的时间: 
struct tim. {   
 long    tv_sec;                 /* 秒(1000000微秒) */ 
long    tv_usec;                 /* 微妙 */
}

2.7实战篇2:setitimer的代码演示

本处设计了一个精确定时器的例子,进程每隔1.5秒数发送定时信号SIGPROF,在接收到信号时将打印定时的次数,用户可以键入CTRL_C或DELETE结束程序

#include 
#include 
#include 
#include 
#include 
int n = 0;
void timefunc(int sig) /* 定时事件代码 */
{    
fprintf(stderr, "ITIMER_PROF[%d]\n", n++);
}
void main()
{
 struct itimerval value;   
  value.it_value.tv_sec=1;                /* 定时1.5秒 */    	
  value.it_value.tv_usec=500000;  
value.it_interval.tv_sec=1;             /* 定时1.5秒 */    
value.it_interval.tv_usec=500000;    
signal(SIGALRM, timefunc);         /* 捕获定时信号 */ 
setitimer(ITIMER_REAL, &value, NULL);   /* 定时开始 */ 
while (1);
}

2.8信号的简要说明

信号简要说明:
SIGHUP    终止进程    终端线路挂断
SIGINT    终止进程    中断进程
SIGQUIT   建立CORE文件终止进程,并且生成core文件
SIGILL   建立CORE文件      非法指令
SIGTRAP  建立CORE文件      跟踪自陷
SIGBUS   建立CORE文件      总线错误
SIGSEGV  建立CORE文件      段非法错误
SIGFPE   建立CORE文件      浮点异常
SIGIOT   建立CORE文件      执行I/O自陷
SIGKILL  终止进程    杀死进程
SIGPIPE  终止进程    向一个没有读进程的管道写数据
SIGALARM  终止进程    计时器到时
SIGTERM  终止进程    软件终止信号
SIGSTOP  停止进程    非终端来的停止信号
SIGTSTP  停止进程    终端来的停止信号
SIGCONT  忽略信号    继续执行一个停止的进程
SIGURG   忽略信号    I/O紧急信号
SIGIO    忽略信号    描述符上可以进行I/O
SIGCHLD  忽略信号    当子进程停止或退出时通知父进程
SIGTTOU  停止进程    后台进程写终端
SIGTTIN  停止进程    后台进程读终端
SIGXGPU  终止进程    CPU时限超时
SIGXFSZ  终止进程    文件长度过长
SIGWINCH  忽略信号    窗口大小发生变化
SIGPROF  终止进程    统计分布图用计时器到时
SIGUSR1  终止进程    用户定义信号1
SIGUSR2  终止进程    用户定义信号2
SIGVTALRM 终止进程    虚拟计时器到时

你可能感兴趣的:(linux,嵌入式,linux,单片机,运维)