• 【STM32F4】【银杏科技ARM+FPGA】iCore3移植RT-Thread--内核之线程的创建


      RT-Thread系统中线程是调度的最小单位,后面的内核讲解都是以线程为单位展开的,线程的本质就是我们平时跑的裸机函数,但是它添加了实时性的元素,可以函数级的抢占,但不存在中断嵌套导致栈溢出的问题,因为每个线程都有自己的栈,使得RT-Thread具有实时性。初学者对操作系统的线程不太了解,先从我们接触比较多的引入,以进程开始。

    一、线程的引出

    进程:当一个程序进入内存运行,即变成一个进程,进程是正在运行的程序,并且具有一定独立功能。例如我们的电脑同时打开了word、QQ、迅雷等,每个运行中的程序就是一个进程。

    线程:线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程,一个进程中可以有多个线程。例如我们打开多个QQ的聊天室,每个聊天室就是一个线程。

      我们知道,在跑裸机系统时,程序是放在main函数中while(1)中不断的循环,有顺序地跑里面的功能函数,例如LED的闪烁。如果加入了按键功能去检测外部的事件,且按键功能函数运行时间较长,就会影响到下一个功能函数的实时执行,例如此时另外一个按键按下,但是上一个按键功能还未执行完,即使按键松开后也未被执行,导致整个系统的实时响应能力变差,甚至导致事件丢失。由此中断响应可以将事件的响应和处理分割开来,大大提高了程序的实时响应能力,但是事件的处理还是在外部函数中顺序执行。从而引出多线程系统,虽然多线程系统的事件也是在中断中完成的,但是事件的处理是在线程中完成的,线程之间的切换所需要操作系统做的工作很少,占用的资源较少。

    二、线程的重要属性

    1.RT-Thread线程栈

      在多线程系统中,每个线程是独立互不干扰的,为此为每个线程分配独立的栈空间。在任何时刻,只有一个线程执行,RT-thread调度器决定运行哪个线程,调度器会不断的开启或关闭每一个线程,从表面上看所有的线程是同时执行的。RT-Thread具有独立的栈,用于存放线程的上下文环境(寄存器值、堆栈内容),当线程切换时,用于保存或读取上下文信息,恢复上次的执行环境。

    2.RT-Thread线程状态

    系统运行时同一时间只能允许一个线程运行,每个线程有五种运行状态,系统会自动根据它们的运行情况做调整:

    初始状态(RT_THREAD_INIT):创建线程即为初始状态,此时不参与系统调度。

    就绪状态(RT_THREAD_READY):该线程在就绪列表中,根据优先级等待CPU。

    运行状态(RT_THREAD_RUNNING):线程当前正在运行。

    挂起状态(RT_THREAD_SUSPEND):若线程正在等待某个时序或外部中断,线程被挂起,此时线程不参与调度。

    关闭状态(RT_THREAD_CLOSE):该线程运行结束时将出于关闭状态。

    3.线程调度

      线程根据线程的优先级调度,RT-Thread最大支持256个线程优先级(0~255),数值越小优先级越高,对于 ARM Cortex-M 系列,普遍采用 32 个优先级。最低优先级默认分配给空闲线程使用,用户一般不使用。在系统中,当有比当前线程优先级更高的线程就绪时,当前线程将立刻被换出,高优先级线程抢占处理器运行。

    4.线程时间片

      RT-Thread只对优先级相同的就绪线程采用时间片轮转的方式调度,时间片起到约束线程单词运行时长的作用。为了实现线程轮转调度,系统把所有就绪的线程先入先出的原则进行排列,每当执行线程调度时,让它在CPU上运行一个时间片的时间,实现线程的轮转运行。

    三、线程状态切换

      RT-Thread提供一系列的操作系统调用接口,使得线程的状态在这五个状态之间来回切换。

    线程通过调用函数 rt_thread_create/init() 进入到初始状态(RT_THREAD_INIT);

    初始状态的线程通过调用函数 rt_thread_startup() 进入到就绪状态(RT_THREAD_READY);就绪状态的线程被调度器调度后进入运行状态(RT_THREAD_RUNNING);当处于运行状态的线程调用①中函数或者获取不到资源时,将进入到挂起状态(RT_THREAD_SUSPEND);

    处于挂起状态的线程,如果等待超时依然未能获得资源或由于其他线程释放了资源,那么它将返回到就绪状态。挂起状态的线程,如果调用 rt_thread_delete/detach() 函数,将更改为关闭状态(RT_THREAD_CLOSE);

    而运行状态的线程,如果运行结束,就会在线程的最后部分执行 rt_thread_exit() 函数,将状态更改为关闭状态。

    四、线程管理

      线程的相关操作包含:创建/初始化线程、启动线程、运行线程、删除/脱离线程。可以使用rt_thread_create()创建一个动态线程,使用rt_thread_init()初始化一个静态线程,动态线程与静态线程的区别是:动态线程是系统自动从动态内存堆上分配栈空间与线程句柄,静态线程是由用户分配栈空间与线程句柄。我们重点来讲动态线程和静态线程的创建和线程的启动。RT-Thread中的线程一般由三部分组成:线程代码、线程控制块、线程堆栈。

      线程控制块由结构体struct rt_thread表示,线程控制块是操作系统用于管理线程的一个数据结构,用于存放优先级、线程名称、线程状态等,也包含线程与线程之间连接用的链表结构。例如为led线程定义一个线程控制块:static struct rt_thread led_thread;

      静态线程和动态线程最大的区别是线程堆栈的使用,线程堆栈是一段连续的内存块,当线程切换后,为了满足线程切换和响应中断时保存CPU寄存器中的内容及调用其他函数时的准备,每个线程都要有自己的堆栈。静态线程会占用RW空间,但是不需要动态分配内存,运行时效率高;动态线程不会占用额外的RW空间,占用空间小,但是运行时需要动态分配内存,效率没有静态方式高。

      启动线程:当线程初始化好后,此时还不能参与操作系统的调度,通过rt_thread_startup()函数将初始化状态转为就绪状态,只有当线程进入就绪状态(RT_THREAD_READY)后才能参与操作系统的调度。

    1、动态线程创建

      使用动态线程时,RT-Thread会动态申请线程控制块和堆栈空间,编译时,编译器不会感知到这段空间,只有在程序运行时,RT-Thread才会从系统堆中申请分配内存空间,当不需要使用该线程时,调用rt_thread_delete会将该申请的内存空间释放。

    rt_thread_t rt_thread_create(const char *name,                //线程名称
                                 void (*entry)(void *parameter),  //线程入口函数
                                 void       *parameter,           //线程入口参数
                                 rt_uint32_t stack_size,          //线程栈大小,单位为字节
                                 rt_uint8_t  priority,            //线程的优先级
                                 rt_uint32_t tick)                //线程的时间片大小

    2、静态线程创建

      使用静态线程时,必须先定义静态的线程控制块,并且定义好堆栈空间,然后调用rt_thread_init来完成线程的初始化,线程控制块和堆栈所用的内存放在RW中,此内存在编译时已被确定,因为不是动态分配的,所以不会被释放,必须使用rt_thread_detach函数将该线程控制块从对象管理器中脱离。

    static rt_err_t _rt_thread_init(struct rt_thread *thread,      //线程句柄
                                    const char       *name,        //线程的名称
                                    void (*entry)(void *parameter),//线程的入口函数
                                    void             *parameter,   //线程入口函数参数
                                    void             *stack_start, //线程栈起始地址
                                    rt_uint32_t       stack_size,  //线程栈大小
                                    rt_uint8_t        priority,    //线程优先级
                                    rt_uint32_t       tick)        //线程的时间片大小

    五、线程应用实例

    具体代码见工程中thread_sample.c。

    #include <rtthread.h>
    #include <rtdevice.h>
    #include <board.h>
    
    /*静态线程相关定义*/
    #define THREAD_PRIORITY    25                     /*优先级 */
    #define STACK_SIZE         512                    /* 栈大小*/
    #define TIMESLICE          5                      /* 时间片*/
    
    /* 静态线程三要素 */
    static rt_uint8_t static_thread_stack[STACK_SIZE];        /* 线程栈       */
    static struct rt_thread static_thread;                    /* 线程控制块   */
    static void static_thread_entry(void* parameter);         /* 线程入口函数 */
    
    /* 动态线程 */
    static void dynamic_thread_entry(void* parameter);        /* 线程入口函数 */
    
    /* 静态线程入口函数 */ 
    static void static_thread_entry(void* parameter)
    {
        rt_uint32_t i = 0;
          rt_kprintf("This is static thread!
    ");
        /* 无限循环*/
        while (1)
        {
            rt_kprintf("static thread count:%d 
    ", ++i);
            /* 等待0.5s,让出cpu权限,切换到其他线程 */
            rt_thread_delay(500);
        }
    }
    /* 动态线程入口函数 */
    static void dynamic_thread_entry(void* parameter)
    {
        rt_uint32_t i;
          rt_kprintf("This is dynamic thread!
    ");
            for (i = 0; i < 5; i++)
            {
              rt_kprintf("dynamic thread count:%d 
    ", i);
            }
    }
    
    static rt_thread_t tid1 = RT_NULL;
    static struct rt_thread static_thread;
    
    int thread_sample(void)
    {
            /*创建静态线程:优先级25,时间片5个系统滴答,线程栈512字节*/
             rt_thread_init(&static_thread,
                            "static_thread",
                            static_thread_entry, 
                            RT_NULL,
                            (rt_uint8_t*)&static_thread_stack[0], 
                            STACK_SIZE, 
                            THREAD_PRIORITY, 
                            TIMESLICE);
    
            /* 创建成功则启动静态线程 */
            rt_thread_startup(&static_thread);
                   
            /* 创建动态线程 : 优先级24,时间片5个系统滴答,线程栈512字节 */
            tid1 = rt_thread_create("dynamic_thread",
                                     dynamic_thread_entry,
                                     RT_NULL,
                                     STACK_SIZE,
                                     THREAD_PRIORITY-1,
                                     TIMESLICE);    
    
            rt_thread_startup(tid1);
    
            return 0;
    }
    /* 导出到 msh 命令列表中 */
    MSH_CMD_EXPORT(thread_sample, thread sample);

    六、实验结果如下:

    打开putty ,选择正确的COM口,设置波特率为115200,下载程序完成后iCore3 ARM_LED三色灯循环闪烁,输入thread_sample命令即可运行。

  • 相关阅读:
    诸神之眼-Nmap(精版,更新中。。。)
    workerman-chat聊天室
    Mysql记录事本
    网站标题、描述、关键词怎么设置?
    什么是谷歌SEO?
    图片加载之性能优化
    前端实现图片懒加载(lazyload)的两种方式
    HTML`CSS_网站页面不同浏览器兼容性问题解决
    从输入url到显示页面,都经历了什么
    什么是mvvm mvc是什么区别 原理
  • 原文地址:https://www.cnblogs.com/xiaomagee/p/13029514.html
Copyright © 2020-2023  润新知