1 微处理器系统
随着产品功能的增多,裸机系统不能够满足产品需求,引入RTOS实时操作系统的多线程管理,可以增加程序的稳定性逻辑性,便于管理;
2 线程
通常默认一个能独立实现功能的函数,称之为线程;多线程管理的意思就是这个程序可以实现多个功能管理;
2.1 线程栈
每个线程都有一个独立的线程栈空间,存储该线程的寄存器状态、数据等;通常是一段内存空间或全局数组;
裸机系统的对象,全部存放在由启动文件初始化的栈空间内;多线程系统的每个线程互不干扰,所以每个线程栈也互不干扰;
rt_uint8_t rt_flag1_thread_stack[512];
rt_uint8_t rt_flag2_thread_stack[512];
2.2 线程控制块struct
每个线程都有一个唯一的线程控制块,是存储该线程的各种参数、地址的结构体;
/*
*************************************************************************
* 线程结构体
*************************************************************************
*/
struct rt_thread
{
void *sp; /* 线程栈指针 cae每个线程都有一个自己的线程控制块struct */
void *entry; /* 线程入口地址 cae通俗来讲就是函数名 */
void *parameter; /* 线程形参 cae通俗来讲就是函数参数*/
void *stack_addr; /* 线程起始地址 cae2.1线程栈起始地址*/
rt_uint32_t stack_size; /* 线程栈大小,单位为字节 cae线程栈stack_addr的大小*/
rt_list_t tlist; /* 线程链表节点 */
};
typedef struct rt_thread *rt_thread_t;
/************************************************************************/
struct rt_list_node
{
struct rt_list_node *next; /* 指向后一个节点 */
struct rt_list_node *prev; /* 指向前一个节点 */
};
typedef struct rt_list_node rt_list_t;
2.3 线程初始化
//thread.c
rt_err_t rt_thread_init(struct rt_thread *thread,
void (*entry)(void *parameter),
void *parameter,
void *stack_start,
rt_uint32_t stack_size)
{
rt_list_init(&(thread->tlist));//rtservice.h
thread->entry = (void *)entry;
thread->parameter = parameter;
thread->stack_addr = stack_start;
thread->stack_size = stack_size;
/* 初始化线程栈,并返回线程栈指针 */
thread->sp = (void *)rt_hw_stack_init( thread->entry,
thread->parameter,
(void *)((char *)thread->stack_addr + thread->stack_size - 4) );
return RT_EOK;
}
//cpuport.c
/* 线程栈初始化 */
rt_uint8_t *rt_hw_stack_init(void *tentry,
void *parameter,
rt_uint8_t *stack_addr)
{
struct stack_frame *stack_frame;
rt_uint8_t *stk;
unsigned long i;
/* 获取栈顶指针
rt_hw_stack_init 在调用的时候,传给stack_addr的是(栈顶指针)*/
stk = stack_addr + sizeof(rt_uint32_t);
/* 让stk指针向下8字节对齐 */
stk = (rt_uint8_t *)RT_ALIGN_DOWN((rt_uint32_t)stk, 8);
/* stk指针继续向下移动sizeof(struct stack_frame)个偏移 */
stk -= sizeof(struct stack_frame);
/* 将stk指针强制转化为stack_frame类型后存到stack_frame */
stack_frame = (struct stack_frame *)stk;
/* 以stack_frame为起始地址,将栈空间里面的sizeof(struct stack_frame)
个内存初始化为0xdeadbeef */
for (i = 0; i < sizeof(struct stack_frame) / sizeof(rt_uint32_t); i ++)
{
((rt_uint32_t *)stack_frame)[i] = 0xdeadbeef;
}
/* 初始化异常发生时自动保存的寄存器 */
stack_frame->exception_stack_frame.r0 = (unsigned long)parameter; /* r0 : argument */
stack_frame->exception_stack_frame.r1 = 0; /* r1 */
stack_frame->exception_stack_frame.r2 = 0; /* r2 */
stack_frame->exception_stack_frame.r3 = 0; /* r3 */
stack_frame->exception_stack_frame.r12 = 0; /* r12 */
stack_frame->exception_stack_frame.lr = 0; /* lr */
stack_frame->exception_stack_frame.pc = (unsigned long)tentry; /* entry point, pc */
stack_frame->exception_stack_frame.psr = 0x01000000L; /* PSR */
/* 返回线程栈指针 */
return stk;
}
struct stack_frame
struct exception_stack_frame
{
/* 异常发生时自动保存的寄存器 */
rt_uint32_t r0;
rt_uint32_t r1;
rt_uint32_t r2;
rt_uint32_t r3;
rt_uint32_t r12;
rt_uint32_t lr;
rt_uint32_t pc;
rt_uint32_t psr;
};
struct stack_frame
{
/* r4 ~ r11 register
异常发生时需手动保存的寄存器 */
rt_uint32_t r4;
rt_uint32_t r5;
rt_uint32_t r6;
rt_uint32_t r7;
rt_uint32_t r8;
rt_uint32_t r9;
rt_uint32_t r10;
rt_uint32_t r11;
struct exception_stack_frame exception_stack_frame;
};
2.4 初始化两个线程的例程
#include <rtthread.h>
#include "ARMCM3.h"
//cae这个数组应该是要用来存储线程节点的;
extern rt_list_t rt_thread_priority_table[RT_THREAD_PRIORITY_MAX];
/* 定义线程控制块 */
struct rt_thread rt_flag1_thread;
struct rt_thread rt_flag2_thread;
ALIGN(RT_ALIGN_SIZE)
/* 定义线程栈 */
rt_uint8_t rt_flag1_thread_stack[512];
rt_uint8_t rt_flag2_thread_stack[512];
/* 线程声明 */
//cae线程函数执行完之后会在函数结尾调用rt_schedule();切换线程;
void flag1_thread_entry(void *p_arg);
void flag2_thread_entry(void *p_arg);
int main(void)
{
/* 硬件初始化 */
/* 将硬件相关的初始化放在这里,如果是软件仿真则没有相关初始化代码 */
/* cae将线程管理的链表变量的数组初始化掉32个全部node,然后为第一个node赋休眠地址rt_thread_defunct */
rt_system_scheduler_init();
/* cae初始化当前线程的rt_thread结构体,初始化线程栈,然后把线程栈顶地址放入rt_thread->sp */
rt_thread_init( &rt_flag1_thread, /* 线程控制块 */
flag1_thread_entry, /* 线程入口地址 */
RT_NULL, /* 线程形参 */
&rt_flag1_thread_stack[0], /* 线程栈起始地址 */
sizeof(rt_flag1_thread_stack) ); /* 线程栈大小,单位为字节 */
/* cae我怎么觉得这里应该是rt_list_insert_after(&table[0], &tlist)??
* rt_thread_priority_table[32]编译时就从低到高分配了连续空间,你插入table[0]前面那不是都插出数组了??
* 大约是因为这个代码只使用了该链表的l->pre,不管了,先放着;顺其自然静待开窍把;*/
rt_list_insert_before( &(rt_thread_priority_table[0]),&(rt_flag1_thread.tlist) );
/* 初始化线程 */
rt_thread_init( &rt_flag2_thread, /* 线程控制块 */
flag2_thread_entry, /* 线程入口地址 */
RT_NULL, /* 线程形参 */
&rt_flag2_thread_stack[0], /* 线程栈起始地址 */
sizeof(rt_flag2_thread_stack) ); /* 线程栈大小,单位为字节 */
rt_list_insert_before( &(rt_thread_priority_table[1]),&(rt_flag2_thread.tlist) );
/* 启动系统调度器 */
rt_system_scheduler_start();
}
2.4.1 scheduler 线程切换
/***
搭配rt_list_t rt_thread_priority_table[32]使用,进行线程的切换;
实际切换还是通过结尾那几个汇编函数切换的,先放着;
***/
/* 产生上下文切换 */
rt_hw_context_switch((rt_uint32_t)&from_thread->sp,(rt_uint32_t)&to_thread->sp);
/* 切换到第一个线程,该函数在context_rvds.S中实现,在rthw.h声明,
用于实现第一次任务切换。当一个汇编函数在C文件中调用的时候,
如果有形参,则执行的时候会将形参传人到CPU寄存器r0。*/
rt_hw_context_switch_to((rt_uint32_t)&to_thread->sp);
scheduler.c
#include <rtthread.h>
#include <rthw.h>
/*
*************************************************************************
* 全局变量
*************************************************************************
*/
/* 线程控制块指针,用于指向当前线程 */
struct rt_thread *rt_current_thread;
/* 线程就绪列表 */
rt_list_t rt_thread_priority_table[RT_THREAD_PRIORITY_MAX];
/* 线程休眠列表 */
rt_list_t rt_thread_defunct;
/*
*************************************************************************
* 函数实现
*************************************************************************
*/
/* 初始化系统调度器 */
void rt_system_scheduler_init(void)
{
register rt_base_t offset;
/* 线程就绪列表初始化 */
for (offset = 0; offset < RT_THREAD_PRIORITY_MAX; offset ++)
{
rt_list_init(&rt_thread_priority_table[offset]);
}
/* 初始化当前线程控制块指针 */
rt_current_thread = RT_NULL;
/* 初始化线程休眠列表,当线程创建好没有启动之前会被放入到这个列表 */
rt_list_init(&rt_thread_defunct);
}
/* 启动系统调度器 */
void rt_system_scheduler_start(void)
{
register struct rt_thread *to_thread;
/* 手动指定第一个运行的线程 */
to_thread = rt_list_entry(rt_thread_priority_table[0].next,
struct rt_thread,
tlist);
rt_current_thread = to_thread;
/* 切换到第一个线程,该函数在context_rvds.S中实现,在rthw.h声明,
用于实现第一次任务切换。当一个汇编函数在C文件中调用的时候,
如果有形参,则执行的时候会将形参传人到CPU寄存器r0。*/
rt_hw_context_switch_to((rt_uint32_t)&to_thread->sp);
}
/* 系统调度 */
//cae flag1_thread_entry(),flag2_thread_entry()程序内部执行完之后会调用rt_schedule()来切换到下一个线程;
void rt_schedule(void)
{
struct rt_thread *to_thread;
struct rt_thread *from_thread;
/* 两个线程轮流切换 */
if( rt_current_thread == rt_list_entry( rt_thread_priority_table[0].next,
struct rt_thread,
tlist) )
{
from_thread = rt_current_thread;
to_thread = rt_list_entry( rt_thread_priority_table[1].next,
struct rt_thread,
tlist);
rt_current_thread = to_thread;
}
else
{
from_thread = rt_current_thread;
to_thread = rt_list_entry( rt_thread_priority_table[0].next,
struct rt_thread,
tlist);
rt_current_thread = to_thread;
}
/* 产生上下文切换 */
rt_hw_context_switch((rt_uint32_t)&from_thread->sp,(rt_uint32_t)&to_thread->sp);
}
2.4.2 node 节点删改
/* 已知一个结构体里面的成员的地址,反推出该结构体的首地址 */
#define rt_container_of(ptr, type, member) \
((type *)((char *)(ptr) - (unsigned long)(&((type *)0)->member)))
#define rt_list_entry(node, type, member) \
rt_container_of(node, type, member)
/***上面这个结构体没看懂,下面的链表初始化就是一个队列初始化;***/
rtservice.h
#ifndef __RT_SERVICE_H__
#define __RT_SERVICE_H__
/* 已知一个结构体里面的成员的地址,反推出该结构体的首地址 */
#define rt_container_of(ptr, type, member) \
((type *)((char *)(ptr) - (unsigned long)(&((type *)0)->member)))
#define rt_list_entry(node, type, member) \
rt_container_of(node, type, member)
/*
*************************************************************************
* 双向链表操作相关函数
* cae链表存储的数据是node地址;node节点里又是前后节点的地址;
*************************************************************************
*/
/* 初始化链表节点 */
rt_inline void rt_list_init(rt_list_t *l)
{
l->next = l->prev = l;
}
/* 在双向链表头部插入一个节点 cae在l节点之后插入n节点 */
rt_inline void rt_list_insert_after(rt_list_t *l, rt_list_t *n)
{
l->next->prev = n;
n->next = l->next;
l->next = n;
n->prev = l;
}
/* 在双向链表尾部插入一个节点 cae在l节点之前插入n节点 */
rt_inline void rt_list_insert_before(rt_list_t *l, rt_list_t *n)
{
l->prev->next = n;
n->prev = l->prev;
l->prev = n;
n->next = l;
}
/* 从双向链表删除一个节点 */
rt_inline void rt_list_remove(rt_list_t *n)
{
n->next->prev = n->prev;
n->prev->next = n->next;
n->next = n->prev = n;
}
#endif /* __RT_SERVICE_H__ */
标签:rt,thread,frame,list,RTthread,STM32,线程,stack From: https://www.cnblogs.com/caesura-k/p/17353515.html