发 帖  
原厂入驻New
[原创] 灵动微课堂 (第135讲) | 基于MM32 MCU的OS移植与应用——RT-Thread 线程管理
2020-9-3 17:40:02  176
分享

线程的概念

线程可以理解为一个个相互独立运行的单元任务。这些任务共同使用CPU和内存空间等系统资源,不过,每一时刻,只能有一个线程在运行。

各个线程的运行是相互独立,不断切换的,所以线程都要有自己的堆栈,用来保护现场和恢复现场,使再次运行时能够从上次暂停的地方继续向后运行。

RT-Thread线程
RT-Thread 线程管理的主要功能是对线程进行管理和调度,系统中总共存在两类线程,分别是系统线程和用户线程,系统线程是由 RT-Thread 内核创建的线程,用户线程是由应用程序创建的线程,这两类线程都会从内核对象容器中分配线程对象,当线程被删除时,也会被从对象容器中删除,如图所示,每个线程都有重要的属性,如线程控制块、线程栈、入口函数等。

4.jpg 图 1 对象容器与线程对象

线程的调度
RT-Thread提供的线程调度器是基于优先级的全抢占式调度,但中断函数和临界段程序是不可抢占的。系统总共支持256个优先级,即0~255,数值越小,优先级越高,0为最高优先级,255是空闲线程的优先级,用户一般不会使用。当有比当前运行线程优先级更高的线程就绪时,当前线程将立刻被换出,高优先级线程抢占运行。

RT-Thread还支持创建相同优先级的线程。这些线程采用时间片轮转进行调度,但前提是没有更高优先级的线程出现。

所有准备好的线程都链接到了优先级队列中,选择优先级高的线程就是在优先级数组中寻找最高优先级线程对应的非空链表。

线程的状态
操作系统中的线程有的在执行,有的被抢占,还有的即将被运行,也就是说有多种运行状态。

RT-Thread将线程分为5种运行状态:

01 初始态

RT_THREAD_INIT,创建线程时设置为初始态
02 就绪态

RT_THREAD_READY,线程在就绪列表中,具备执行的条件,等待CPU运行
03 运行态

RT_THREAD_RUNNING,线程正在被CPU执行
04 挂起态

RT_THREAD_SUSPEND,线程正在等待某个信号量、消息队列、事件或被延时等情况,不在就绪列表
05 关闭态

RT_THREAD_CLOSE,线程运行结束,等待系统回收资源

线程的状态是不断转换的,用一张图来表示线程状态迁移:
3.jpg 图 2 线程状态切换

建立线程实例
下面介绍一下线程创建的步骤,以下发生在main.c源文件中。

01
定义线程控制块和声明线程函数
static rt_thread_t mm32_example_thread = RT_NULL;        //初始化线程控制块
static void mm32_thread_entry(void *parameter);          //线程函数声明

02
在main函数中创建线程
可以使用 rt_thread_create() 创建一个动态线程,或者使用 rt_thread_init() 初始化一个静态线程,动态线程与静态线程的区别是:动态线程是系统自动从动态内存堆上分配栈空间与线程句柄(初始化 heap 之后才能使用create 创建动态线程),静态线程是由用户分配栈空间与线程句柄。

mm32_example_thread =                  //线程控制块指针
rt_thread_create("example",           //线程名字
                  mm32_thread_entry,   //线程入口函数
                  RT_NULL,             //线程入口函数参数
                  512,                //线程栈大小
                  3,                  //线程的优先级
                  20);                 //线程时间片(单位tick)
// 如果成功获取到线程控制块,则启动线程,开启调度
IF(mm32_example_thread != RT_NULL)
     rt_thread_startup(mm32_example_thread);
else
     return -1;

03
编写线程函数内容
设计一个无限循环的作为线程函数。无限循环的目的就是为了让这个线程一直被系统循环调度运行,永不删除。

作为一个优先级明确的实时系统,如果一个线程中的程序陷入了没有释放CPU使用权的循环操作,那么比它优先级低的线程都将不能够得到执行。所以在实时操作系统中必须注意的一点就是:线程中必须要有让出 CPU使用权的动作,如在循环中调用等待事件的操作、调用延时函数或者主动挂起。

static void mm32_thread_entry(void *parameter)
{
   while(1)             // 无限循环,该线程不会被系统删除,
                          // 若无这个循环,则线程运行完毕后会被系统自动删除
   {                  
      Wait_Event();    // 操作系统在这里释放CPU去执行其它线程,当事件发生再继续执行
      Do_Something();  // 对事件进行服务、进行处理   
   }
}

通过以上三个步骤,就能建立可运行的线程了。

线程还有其他一些操作操作函数,如:

线程删除函数
rt_thread_delete(rt_thread_t thread):
用于rt_thread_create创建的线程
线程脱离函数
rt_thread_detach(rt_thread_t thread):
用于rt_thread_init()初始化的线程
线程挂起函数
rt_thread_suspend(rt_thread_t thread);
线程恢复函数
rt_thread_resume(rt_thread_t thread);
线程启动函数
rt_thread_startup(rt_thread_t thread);
线程获取函数
rt_thread_self(void);

除了以上操作函数,另外还有使线程让出处理器资源函数、使线程睡眠函数、控制线程函数、设置和删除空闲钩子函数、设置调度器钩子函数等,用户可以根据实际的应用需求选择对应的线程调用函数。

示例代码
#define MM32_THREAD_STACK_SIZE   512   /* 线程堆栈大小 */
#define MM32_THREAD_PRIORITY     5     /* 线程优先级 */
#define MM32_THREAD_TIMESLICE    10   /* 线程轮转时间片 */

static rt_thread_t mm32_thread1 = RT_NULL;
static char mb_str[] = "Hello MM32!";              /* 邮箱内容 */

/* 线程1入口函数:实现LED1灯的翻转和发送邮件信息 */
static void mm32_thread1_entry(void *parameter)
{
    /* 初始化LED灯控制端口 */
    mm32_led_init();

    while(1)
    {
      /* LED灯翻转显示 */
      mm32_led_toggle(LED1);
      /* 发送mb_str地址到邮箱中 */
      rt_mb_send(&mm32_mb, (rt_uint32_t)&mb_str);
      /* 打印提示消息 */
      rt_kprintf("\r\nLED Toggle and Send Mailbox\r\n");
      /* 处理500毫秒,释放MCU使用权,供内核调度 */
      rt_thread_mdelay(500);
    }
}

ALIGN(RT_ALIGN_SIZE)
static char mm32_thread2_stack[1024];
static struct rt_thread mm32_thread2;

static struct rt_mailbox mm32_mb;
static char mm32_mb_poll[128];

/* 线程2入口函数:实现邮箱的初始化、接收邮件并打印显示在终端 */
static void mm32_thread2_entry(void *parameter)
{
    char *str;
    rt_err_t result;

    /* 初始化邮箱 */
    result = rt_mb_init(&mm32_mb,
                        "mm32_mb",
                        &mm32_mb_poll[0], sizeof(mm32_mb_poll)/4,
                        RT_IPC_FLAG_FIFO);
    /* 判断邮箱是否初始化成功 */
    if(result != RT_EOK)
    {
        rt_kprintf("Init Mailbox Failed!\r\n");
        return;
    }

    while(1)
    {
            /* 从邮箱中收取邮件 */
        if(rt_mb_recv(&mm32_mb, (rt_uint32_t *)&str, RT_WAITING_FOREVER) == RT_EOK)
        {
            /* 打印邮件内容 */
            rt_kprintf("Mailbox Received: %s\r\n", str);

            rt_thread_mdelay(100);
        }
    }
}

int main(void)
{
    /* 动态创建mm32_thread1线程 */
    mm32_thread1 = rt_thread_create("thread1",
                                    mm32_thread1_entry, RT_NULL,
                                    MM32_THREAD_STACK_SIZE,
                                    MM32_THREAD_PRIORITY,
                                    MM32_THREAD_TIMESLICE);
    /* 如果线程动态创建成功,则启动该线程 */
    if(mm32_thread1 != RT_NULL)
    {
        rt_thread_startup(mm32_thread1);
    }

    /* 静态初始化mm_thread2线程 */
    rt_thread_init(&mm32_thread2, "thread2",
                   mm32_thread2_entry, RT_NULL,
                   &mm32_thread2_stack[0],
                   sizeof(mm32_thread2_stack),
                   MM32_THREAD_PRIORITY - 1,
                   MM32_THREAD_TIMESLICE);
    /* 启动静态线程 */
    rt_thread_startup(&mm32_thread2);

    return 0;
}

示例代码
添加上述线程管理后,参考之前“灵动MM32MCU”官方公众号的RT-Thread UART数据收发章节,将程序下载到芯片后输出显示结果如下:
2.jpg 图 3 运行结果


2020灵动MM32协作大会

2020年9月10日 9:00
深圳星河丽思卡尔顿酒店
“2020灵动MM32协作大会”重磅开启!

大会内容:
新品发布 技术分享
市场分析行业专家热点剖析
合作伙伴现场秀、互动小游戏
热销“MM32 INSIDE”应用案例展演等

更多精彩等着您,欢迎扫码报名参会!
1.jpg

0
2020-9-3 17:40:02   评论 分享淘帖

只有小组成员才能发言,加入小组>>

1508个成员聚集在这个小组

加入小组

创建小组步骤

关闭

站长推荐 上一条 /8 下一条

快速回复 返回顶部 返回列表