修改!该文变更为快速查阅编程文档,RTT进程实现分析将另起一文
动态创建线程
//动态创建线程
static rt_thread_t tid1 = RT_NULL;
static void thread_entry(void *parameter)
{
}
void thread_init(void)
{
tid1 = rt_thread_create("thread1",
thread1_entry,
RT_NULL,
1024,
5, 10);
if (tid1 != RT_NULL)
rt_thread_startup(tid1);
}
静态创建线程
ALIGN(RT_ALIGN_SIZE)
static char thread_stack[1024];
static struct rt_thread thread;
static void thread_entry(void *param)
{
}
void thread_init(void)
{
rt_thread_init(&thread,
"thread",
thread_entry,
RT_NULL,
&thread_stack[0],
sizeof(thread_stack),
5, 10);
rt_thread_startup(&thread);
}
事件
//
#define EVENT_ALL 0xFFFFFFFF
#define EVENT_CMD (uint32_t)1<<0
/* 事件控制块 */
static struct rt_event event;
/*提前初始化*/
rt_event_init(&event,"event",RT_IPC_FLAG_FIFO);
/*发送事件*/
rt_event_send(&event,EVENT_CMD);
static void thread_recv_event(void *param)
{
uint32_t e;
while(1)
{
rt_event_recv(&event, (EVENT_ALL),RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR,RT_WAITING_FOREVER, &e);
if(e& EVENT_CMD)
{}
}
}
消息队列
- 定义
static struct rt_messagequeue lora_download_msg;//发送给lora的命令存储buffer
static rt_uint8_t download_msg_pool[1024];
- 初始化
result = rt_mq_init(&lora_download_msg,"dw-msg",&download_msg_pool[0], 200,sizeof(download_msg_pool), RT_IPC_FLAG_FIFO);
if (result != RT_EOK)
{
rt_kprintf("init lora_download_msg queue failed.\n");
return;
}
- 使用
//读取消息
struct lora_buffer temp;
if (rt_mq_recv(&lora_download_msg, &temp, sizeof(struct lora_buffer), RT_WAITING_FOREVER) == RT_EOK)
{
rt_kprintf("lora get mq totle %d data:%s\n",lora_download_msg.entry,temp.data);
}
//存入消息
struct lora_buffer msg;
if(rt_mq_send(&lora_download_msg,&msg,sizeof(struct lora_buffer)) != RT_EOK)
{
rt_kprintf("mq_send download error\n");
}
下列文本待删除
该笔记类别主要是在自己学习时做的一些记录,方便自己很久不用忘掉时进行快速回忆
1线程控制块
结构体:
/* rt_thread_t线程句柄,指向线程控制块的指针 */
typedef struct rt_thread* rt_thread_t;
struct rt_thread
{
/* RT-Thread根对象定义 */
char name[RT_NAME_MAX]; /* 对象的名称*/
rt_uint8_t type; /* 对象的类型*/
rt_uint8_t flags; /* 对象的参数*/
#ifdef RT_USING_MODULE
void *module_id; /* 线程所在的模块ID*/
#endif
rt_list_t list; /* 对象链表*/
rt_list_t tlist; /* 线程链表*/
/* 栈指针及入口 */
void* sp; /* 线程的栈指针*/
void* entry; /* 线程入口*/
void* parameter; /* 线程入口参数*/
void* stack_addr; /* 线程栈地址*/
rt_uint16_t stack_size; /* 线程栈大小*/
rt_err_t error; /* 线程错误号*/
rt_uint8_t stat; /* 线程状态 */
/* 优先级相关域 */
rt_uint8_t current_priority; /* 当前优先级*/
rt_uint8_t init_priority; /* 初始线程优先级*/
#if RT_THREAD_PRIORITY_MAX > 32
rt_uint8_t number;
rt_uint8_t high_mask;
#endif
rt_uint32_t number_mask;
#if defined(RT_USING_EVENT)
/* 事件相关域 */
rt_uint32_t event_set;
rt_uint8_t event_info;
#endif
rt_ubase_t init_tick; /* 线程初始tick*/
rt_ubase_t remaining_tick; /* 线程当次运行剩余tick */
struct rt_timer thread_timer; /* 线程定时器*/
/* 当线程退出时,需要执行的清理函数 */
void (*cleanup)(struct rt_thread *tid);
rt_uint32_t user_data; /* 用户数据*/
};
2线程状态
线程状态改变:
enter image description here
- rt_thread_create/init进入到初始状态(RT_THREAD_INIT)
- rt_thread_startup进入到就绪状态(RT_THREAD_READY)
- 当处于就绪状态的线程调用rt_thread_delay,rt_sem_take,rt_mb_recv等函数或由于获取不到资源时,将进入到挂起状态(RT_THREAD_SUSPEND)
- 处于挂起状态的线程,如果等待超时依然未能获得资源或由于其他线程释放了资源,那么它将返回到就绪状态
- 挂起状态的线程,如果调用rt_thread_delete/detach将更改为关闭状态(RT_THREAD_CLOSE)
- 而运行状态的线程,如果运行结束会在线程最后部分执行rt_thread_exit函数而更改为关闭状态(RT_THREAD_CLOSE)
3 相关函数
3.1 简述
函数名称 | 功能 | 描述 |
---|---|---|
rt_system_scheduler_init | 调度器初始化 | |
rt_system_scheduler_start | 启动调度器 | 在调用这个函数时,它会查找系统中优先级最高的就绪态线程,然后切换过去执行。另外在调用这个函数前,必须先做idle线程的初始化,即保证系统至少能够找到一个就绪状态的线程执行。此函数是永远不会返回的 |
rt_schedule | 执行调度 | 调用这个函数后,系统会计算一次系统中就绪态的线程,如果存在比当前线程更高优先级的线程时,系统将切换到高优先级的线程去。上层应用程序一般不需要调用这个函数。 |
rt_scheduler_sethook | 设置调度器钩子 | 用于在线程切换时被调用 |
rt_thread_create | 创建线程 | 系统会从动态堆内存中分配一个线程句柄(即TCB,线程控制块)以及按照参数中指定的栈大小从动态堆内存中分配相应的空间。分配出来的栈空间是按照rtconfig.h中配置的RT_ALIGN_SIZE方式对齐。 |
rt_thread_delete | 线程删除 | 调用该函数后,线程对象将会被移出线程队列并且从内核对象管理器中删除,线程占用的堆栈空间也会被释放,收回的空间将重新用于其他的内存分配。 实际上,用rt_thread_delete函数删除线程接口,仅仅是把相应的线程状态更改为RT_THREAD_CLOSE状态,然后放入到rt_thread_defunct队列中;而真正的删除动作(释放线程控制块和释放线程栈)需要到下一次执行idle线程时,由idle线程完成最后的线程删除动作。用rt_thread_init初始化的静态线程则不能使用此接口删除 |
rt_thread_init | 静态线程初始化 | 函数用来初始化静态线程对象。而线程句柄(或者说线程控制块指针),线程栈由用户提供。静态线程是指,线程控制块、线程运行栈一般都设置为全局变量,在编译时就被确定、被分配处理,内核不负责动态分配内存空间。需要注意的是,用户提供的栈首地址需做系统对齐(例如ARM上需要做4字节对齐)。 |
rt_thread_detach | 针对静态线程进行脱落 | 这个函数接口是和rt_thread_delete()函数相对应的, rt_thread_delete()函数操作的对象是rt_thread_create()创建的句柄,而rt_thread_detach()函数操作的对象是使用rt_thread_init()函数初始化的线程控制块。同样,线程本身不应调用这个接口脱离线程本身 |
rt_thread_startup | 线程启动 | 当调用这个函数时,将把线程的状态更改为就绪状态,并放到相应优先级队列中等待调度。如果新启动的线程优先级比当前线程优先级高,将立刻切换到这个线程 |
rt_thread_self | 获取当前线程句柄 | 在程序的运行过程中,相同的一段代码可能会被多个线程执行,在执行的时候可以通过下面的函数接口获得当前执行的线程句柄 |
rt_thread_yield | 线程让出处理器 | 调用该函数后,当前线程首先把自己从它所在的就绪优先级线程队列中删除,然后把自己挂到这个优先级队列链表的尾部,然后激活调度器进行线程上下文切换(如果当前优先级只有这一个线程,则这个线程继续执行,不进行上下文切换动作) |
rt_thread_sleep rt_thread_delay | 线程睡眠 | 这两个函数接口的作用相同,调用它们可以使当前线程挂起一段指定的时间,当这个时间过后,线程会被唤醒并再次进入就绪状态。这个函数接受一个参数,该参数指定了线程的休眠时间(单位是OS Tick时钟节拍) |
rt_thread_suspend | 线程挂起 | 当线程调用rt_thread_delay,调用线程将主动挂起,当调用rt_sem_take,rt_mb_recv等函数时,资源不可使用也将导致调用线程挂起。处于挂起状态的线程,如果其等待的资源超时(超过其设定的等待时间),那么该线程将不再等待这些资源,并返回到就绪状态;或者,当其它线程释放掉该线程所等待的资源时,该线程也会返回到就绪状态 |
rt_thread_resume | 线程恢复 | 线程恢复就是让挂起的线程重新进入就绪状态 |
rt_thread_control | 线程控制 | 当需要对线程进行一些其他控制时,例如动态更改线程的优先级 |
rt_thread_idle_init | 初始化空闲线程 | 根据前面的描述,系统运行过程中必须存在一个最终可运行的线程 |
rt_thread_idle_sethook | 设置空闲线程钩子 | 当空闲线程运行时会自动执行设置的钩子函数,由于空闲线程具有系统的最低优先级,所以只有在空闲的时候才会执行此钩子函数。空闲线程是一个线程状态永远为就绪态的线程,因此设置的钩子函数必须保证空闲线程在任何时刻都不会处于挂起状态 |
3.2 使用
rt_thread_create 创建线程
rt_thread_t rt_thread_create(
const char* name,//线程的名称;线程名称的最大长度由rtconfig.h中定义的RT_NAME_MAX宏指定,多余部分会被自动截掉
void (*entry)(void* parameter),//线程入口函数
void* parameter,//线程入口函数参数
rt_uint32_t stack_size,//线程栈大小,单位是字节。在大多数系统中需要做栈空间地址对齐
rt_uint8_t priority, //线程的优先级。优先级范围根据系统配置情况(rtconfig.h中的RT_THREAD_PRIORITY_MAX宏定义),如果支持的是256级优先级,那么范围是从0 ~ 255,数值越小优先级越高,0代表最高优先级
rt_uint32_t tick//线程的时间片大小。时间片(tick)的单位是操作系统的时钟节拍。当系统中存在相同优先级线程时,这个参数指定线程一次调度能够运行的最大时间长度。这个时间片运行结束时,调度器自动选择下一个就绪态的同优先级线程进行运行
);
进程成功则返回相应句柄,否则返回RT_NULL。
使用举例,例程根据上个博文的串口打印改写:
#include <rtthread.h>
#include <string.h>
static rt_device_t uart_device = RT_NULL;
static char* str ="lissettecarlr";
static uint8_t rcvBuff[100];
int a=0;
static rt_err_t uart_intput(rt_device_t dev, rt_size_t size)
{
return RT_EOK;
}
void uart_thread_entry(void *paramter)
{
rt_kprintf("uart_thread\n");
uart_device=rt_device_find("uart2");
rt_device_open(uart_device, RT_DEVICE_FLAG_RDONLY | RT_DEVICE_FLAG_INT_RX );
rt_device_set_rx_indicate(uart_device, uart_intput);
rt_device_write(uart_device, 0, str, strlen(str));
}
int main(void)
{
rt_kprintf("main\n");
rt_thread_t uart_hander;
uart_hander = rt_thread_create("uart_thd"
,uart_thread_entry
,RT_NULL
,1024
,2
,10
);
if (uart_hander != RT_NULL)
{
rt_thread_startup(uart_hander);
}
return 0;
}
rt_thread_delete线程删除
在线程运行完成,自动结束的情况下,系统会自动删除线程,不需要再调用rt_thread_delete()函数接口。这个接口不应由线程本身来调用以删除线程自身,一般只能由其他线程调用或在定时器超时函数中调用。
//参数是线程句柄,返回RT_EOK
rt_err_t rt_thread_delete(rt_thread_t thread);
rt_thread_startup启动线程
//参数是线程句柄,返回RT_EOK
rt_err_t rt_thread_startup(rt_thread_t thread);
rt_thread_self当前线程
通过该程序可以获取到在调用时刻是哪一个进程在执行,请不要在中断服务程序中调用此函数,因为它并不能准确获得当前的执行线程。当调度器未启动时,这个接口返回RT_NULL
//返回当前运行的线程句柄。如果调度器还未启动,将返回RT_NULL
rt_thread_t rt_thread_self(void);
rt_thread_suspend 线程挂起
将指定线程进入挂起态
通常不应该使用这个函数来挂起线程本身,如果确实需要采用rt_thread_suspend函数挂起当前任务,需要在调用rt_thread_suspend()函数后立刻调用rt_schedule()函数进行手动的线程上下文切换
//如果这个线程的状态并不是就绪状态,将返回-RT_ERROR,否则返回RT_EOK
rt_err_t rt_thread_suspend (rt_thread_t thread);
rt_thread_resume 线程恢复
线程恢复就是让挂起的线程重新进入就绪状态,如果被恢复线程在所有就绪态线程中,位于最高优先级链表的第一位,那么系统将进行线程上下文的切换
//如果恢复的线程状态并不是RT_THREAD_SUSPEND状态,将返回-RT_ERROR;否则返回RT_EOK
rt_err_t rt_thread_resume (rt_thread_t thread);
rt_thread_control线程控制
//cmd:
//RT_THREAD_CTRL_CHANGE_PRIORITY - 动态更改线程的优先级;
//RT_THREAD_CTRL_STARTUP - 开始运行一个线程,等同于rt_thread_startup() 函数调用;
//RT_THREAD_CTRL_CLOSE - 关闭一个线程,等同于rt_thread_delete()函数调用。
//控制执行正确返回RT_EOK,否则返回RT_ERROR
rt_err_t rt_thread_control(rt_thread_t thread, rt_uint8_t cmd, void* arg);
4 启动
在编写自己的代码时,通常单独起一个文件,而不是在主函数中写,为了方便调用用户函数,可以使用INIT_APP_EXPORT宏将其加入的启动运行队列中
//其中timer_sample_init是需要运行的用户函数
INIT_APP_EXPORT(timer_sample_init);
网友评论