0.内存堆和栈的使用
堆(Heap):一般由程序员分配和释放
分配:rt_malloc、rt_calloc、rt_realloc
释放:rt_free
内存初始化:rt_memset
栈(Stack):由编译器自动分配和释放
局部变量、全局变量
1.RT-Thread 的线程介绍
RT-Thread 的线程调度器是抢占式的,主要的工作就是从就绪线程列表中查找最高优先级线程,保证最高优先级的线程能够被运行,最高优先级的任务一旦就绪,总能得到 CPU 的使用权,当一个运行着的线程使一个比它优先级高的线程满足运行条件,当前线程的 CPU 使用权就被剥夺了,或者说被让出了,高优先级的线程立刻得到了 CPU 的使用权。
如果是中断服务程序使一个高优先级的线程满足运行条件,中断完成时,被中断的线程挂起,优先级高的线程开始运行。
当调度器调度线程切换时,先将当前线程上下文保存起来,当再切回到这个线程时,线程调度器将该线程的上下文信息恢复。
这里的上下文指的是:线程执行时候的环境,具体来说:变量、数据、所有寄存器变量、堆变量、栈变量、内存信息等
2.线程的状态
2.1初始状态
当线程刚开始创建还没开始运行时就处于初始状态;在初始状态下,线程不参与调度。此状
态在 RT-Thread 中的宏定义为 RT_THREAD_INIT
2.2就绪状态
在就绪状态下,线程按照优先级排队,等待被执行;一旦当前线程运行完毕让出处理器,操
作系统会马上寻找最高优先级的就绪态线程运行。此状态在 RT-Thread 中的宏定义为
RT_THREAD_READY
2.3运行状态
线程当前正在运行。在单核系统中,只有 rt_thread_self() 函数返回的线程处于运行状态;
在多核系统中,可能就不止这一个线程处于运行状态。此状态在 RT-Thread 中的宏定义为
RT_THREAD_RUNNING
2.4挂起状态
也称阻塞态。它可能因为资源不可用而挂起等待,或线程主动延时一段时间而挂起。在挂起
状态下,线程不参与调度。此状态在 RT-Thread 中的宏定义为 RT_THREAD_SUSPEND
3.RT_Thread线程管理结构体
/**
* Thread structure
*/
struct rt_thread
{
/* rt object */
char name[RT_NAME_MAX]; /**< the name of thread */
rt_uint8_t type; /**< type of object */
rt_uint8_t flags; /**< thread's flags */
#ifdef RT_USING_MODULE
void *module_id; /**< id of application module */
#endif
rt_list_t list; /**< the object list */
rt_list_t tlist; /**< the thread list */
/* stack point and entry */
void *sp; /**< stack point */
void *entry; /**< entry */
void *parameter; /**< parameter */
void *stack_addr; /**< stack address */
rt_uint32_t stack_size; /**< stack size */
/* error code */
rt_err_t error; /**< error code */
rt_uint8_t stat; /**< thread status */
/* priority */
rt_uint8_t current_priority; /**< current priority */
rt_uint8_t init_priority; /**< initialized priority */
#if RT_THREAD_PRIORITY_MAX > 32 /*RT_Thread默认支持256个优先级,但是STM32平台默认给32个优先等级 */
rt_uint8_t number;
rt_uint8_t high_mask;
#endif
rt_uint32_t number_mask;
#if defined(RT_USING_EVENT)
/* thread event */
rt_uint32_t event_set;
rt_uint8_t event_info;
#endif
#if defined(RT_USING_SIGNALS)
rt_sigset_t sig_pending; /**< the pending signals */
rt_sigset_t sig_mask; /**< the mask bits of signal */
void *sig_ret; /**< the return stack pointer from signal */
rt_sighandler_t *sig_vectors; /**< vectors of signal handler */
void *si_list; /**< the signal infor list */
#endif
rt_ubase_t init_tick; /**< thread's initialized tick */
rt_ubase_t remaining_tick; /**< remaining tick */
struct rt_timer thread_timer; /**< built-in thread timer */
void (*cleanup)(struct rt_thread *tid); /**< cleanup function when thread exit */
/* light weight process if present */
#ifdef RT_USING_LWP
void *lwp;
#endif
rt_uint32_t user_data; /**< private user data beyond this thread */
};
typedef struct rt_thread *rt_thread_t; /*线程句柄指针*/
4.线程创建
4.1动态创建一个线程
1. 不需要创建线程栈的起始地址
2. 线程句柄由系统自动分配
/**
* This function will create a thread object and allocate thread object memory
* and stack.
*
* @param name the name of thread, which shall be unique 线程名称
* @param entry the entry function of thread 线程执行方法
* @param parameter the parameter of thread enter function 线程执行方法参数
* @param stack_size the size of thread stack 线程栈大小
* @param priority the priority of thread 线程优先等级
* @param tick the time slice if there are same priority thread 时间片
*
* @return the created thread object 线程句柄
*/
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)
4.2静态创建一个线程
1. 需要创建线程栈的起始地址
2. 线程句柄由程序员定义
/**
* This function will initialize a thread, normally it's used to initialize a
* static thread object.
*
* @param thread the static thread object 线程句柄
* @param name the name of thread, which shall be unique 线程名称
* @param entry the entry function of thread 线程执行方法
* @param parameter the parameter of thread enter function 线程执行方法参数
* @param stack_start the start address of thread stack 线程栈起始地址
* @param stack_size the size of thread stack 线程栈大小
* @param priority the priority of thread 优先等级
* @param tick the time slice if there are same priority thread 时间片
*
* @return the operation status, RT_EOK on OK, -RT_ERROR on error
*/
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)
5.线程启动
/**
* This function will start a thread and put it to system ready queue
*
* @param thread the thread to be started
*
* @return the operation status, RT_EOK on OK, -RT_ERROR on error
*/
rt_err_t rt_thread_startup(rt_thread_t thread)
6.释放线程句柄
采用静态方式创建线程这种方式,线程控制块和堆栈占用的内存会放在 RW/ZI 段,这段空间在编译时就已经确定,它不是可以动态分配的,所以不能被释放,而只能使用 rt_thread_detach()函数将该线程控制块从对象管理器中脱离。
使用动态定义方式 rt_thread_create()时, RT-Thread 会动态申请线程控制块和堆栈空间。在编译时,编译器是不会感知到这段空间的,只有在程序运行时, RT-Thread 才会从系统堆中申请分配这段内存空间,当不需要使用该线程时,调用 rt_thread_delete()函数就会将这段申请的内存空间重新释放到内存堆中。
这两种方式各有利弊,静态定义方式会占用 RW/ZI 空间,但是不需要动态分配内存,运行时效率较高,实时性较好。
动态方式不会占用额外的 RW/ZI 空间,占用空间小,但是运行时需要动态分配内存,效率没有静态方式高。
/**
* This function will detach a thread. The thread object will be removed from
* thread queue and detached/deleted from system object management.
*
* @param thread the thread to be deleted
*
* @return the operation status, RT_EOK on OK, -RT_ERROR on error
*/
rt_err_t rt_thread_detach(rt_thread_t thread)
/**
* This function will delete a thread. The thread object will be removed from
* thread queue and deleted from system object management in the idle thread.
*
* @param thread the thread to be deleted
*
* @return the operation status, RT_EOK on OK, -RT_ERROR on error
*/
rt_err_t rt_thread_delete(rt_thread_t thread)
7.线程睡眠
线程睡眠的时间:sleep/delay 的传入参数 tick 以 1 个 OS Tick 为单位;mdelay 的传入参数 ms 以 1ms 为单位;
rt_err_t rt_thread_sleep(rt_tick_t tick);
rt_err_t rt_thread_delay(rt_tick_t tick);
rt_err_t rt_thread_mdelay(rt_int32_t ms);
8.线程让出系统资源
当前线程的时间片用完或者该线程主动要求让出处理器资源时,它将不再占有处理器,调度器会选择相同优先级的下一个线程执行。
rt_err_t rt_thread_yield(void);
9.主动挂起线程
一般不会轻易的主动挂起线程,不推荐这样做。
rt_err_t rt_thread_suspend (rt_thread_t thread);
10.恢复线程
恢复线程就是让挂起的线程重新进入就绪状态,并将线程放入系统的就绪队列中;如果被恢复线程在所有就绪态线程中,位于最高优先级链表的第一位,那么系统将进行线程上下文的切换。
rt_err_t rt_thread_resume (rt_thread_t thread);
11.来自官网的例程:
/*
* Copyright (c) 2006-2018, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2018-08-24 yangjie the first version
*/
/*
* 程序清单:创建/删除、初始化线程
*
* 这个例子会创建两个线程,一个动态线程,一个静态线程。
* 一个线程在运行完毕后自动被系统删除,另一个线程一直打印计数。
*/
#include <rtthread.h>
#define THREAD_PRIORITY 25
#define THREAD_STACK_SIZE 512
#define THREAD_TIMESLICE 5
static rt_thread_t tid1 = RT_NULL;
/* 线程1的入口函数 */
static void thread1_entry(void *parameter)
{
rt_uint32_t count = 0;
while (1)
{
/* 线程1采用低优先级运行,一直打印计数值 */
rt_kprintf("thread1 count: %d\n", count ++);
rt_thread_mdelay(500);
}
}
ALIGN(RT_ALIGN_SIZE)
static char thread2_stack[1024];
static struct rt_thread thread2;
/* 线程2入口 */
static void thread2_entry(void *param)
{
rt_uint32_t count = 0;
/* 线程2拥有较高的优先级,以抢占线程1而获得执行 */
for (count = 0; count < 10 ; count++)
{
/* 线程2打印计数值 */
rt_kprintf("thread2 count: %d\n", count);
}
rt_kprintf("thread2 exit\n");
/* 线程2运行结束后也将自动被系统删除
(线程控制块和线程栈依然在idle线程中释放) */
}
/* 删除线程示例的初始化 */
int thread_sample(void)
{
/* 创建线程1,名称是thread1,入口是thread1_entry*/
tid1 = rt_thread_create("thread1",
thread1_entry, RT_NULL,
THREAD_STACK_SIZE,
THREAD_PRIORITY, THREAD_TIMESLICE);
/* 如果获得线程控制块,启动这个线程 */
if (tid1 != RT_NULL)
rt_thread_startup(tid1);
/* 初始化线程2,名称是thread2,入口是thread2_entry */
rt_thread_init(&thread2,
"thread2",
thread2_entry,
RT_NULL,
&thread2_stack[0],
sizeof(thread2_stack),
THREAD_PRIORITY - 1, THREAD_TIMESLICE);
rt_thread_startup(&thread2);
return 0;
}
/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(thread_sample, thread sample);