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

rtthread与Android系统对比 rtthread与linux区别_初始化


和裸机的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)操作
  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 <timer/mytimer.h>

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

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

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

#include <signal.h>
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 <stdio.h>
#include <unistd.h>
#include <signal.h>
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 <sys/select.h>
#include <sys/time.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
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 终止进程    虚拟计时器到时