RT-Thread内核入门(1)-----线程管理

简述堆栈

栈(stack):由编译器自动分配释放
堆(heap):一般由程序员分配和释放

char *p1;
int main()
{
 	int b;//栈
 	p1=(char)*malloc(10);//堆
}

线程

一个任务被分解成多个子任务,这些子任务就可以看做是线程,线程是实现任务的载体,是RT-Thread中最基本的调度单位,线程运行时认为自己独占cpu运行,线程运行的环境是上下文,包括变量和数据。
线程管理的功能特点
线程管理的主要功能是对线程进行调度和管理,RT-Thread一共有两种线程,一类是系统线程,一类是用户线程,系统线程是系统内核创建的线程,用户线程是用户程序创建的线程
1:线程调度是抢占式调度,优先级高的线程先执行
2:如果是中断服务程序使一个高优先级的线程满足运行条件,中断完成时,被中断的线程挂起,优先级高的线程开始运行。
3:调度器线程切换时,保留该线程的上下文,当切换回该程序时,线程上下文恢复
线程控制块
在RT-Thread中线程控制块由结构体 struct rt_thread表示(rtdef.h),线程控制块是操作系统用于管理线程的一个数据结构,它会存放线程的一些信息,例如优先级、线程名称、线程状态等,也包含线程与线程之间连接用的链表结构,线程等待事件集合等

/* 线程控制块 */
struct rt_thread
{
    /* rt 对象 */
    char        name[RT_NAME_MAX];     /* 线程名称 */
    rt_uint8_t  type;                   /* 对象类型 */
    rt_uint8_t  flags;                  /* 标志位 */

    rt_list_t   list;                   /* 对象列表 */
    rt_list_t   tlist;                  /* 线程列表 */

    /* 栈指针与入口指针 */
    void       *sp;                      /* 栈指针 */
    void       *entry;                   /* 入口函数指针 */
    void       *parameter;              /* 参数 */
    void       *stack_addr;             /* 栈地址指针 */
    rt_uint32_t stack_size;            /* 栈大小 */

    /* 错误代码 */
    rt_err_t    error;                  /* 线程错误代码 */
    rt_uint8_t  stat;                   /* 线程状态 */

    /* 优先级 */
    rt_uint8_t  current_priority;    /* 当前优先级 */
    rt_uint8_t  init_priority;        /* 初始优先级 */
    rt_uint32_t number_mask;

    ......

    rt_ubase_t  init_tick;               /* 线程初始化计数值 */
    rt_ubase_t  remaining_tick;         /* 线程剩余计数值 */

    struct rt_timer thread_timer;      /* 内置线程定时器 */

    void (*cleanup)(struct rt_thread *tid);  /* 线程退出清除函数 */
    rt_uint32_t user_data;                      /* 用户数据 */
};

线程栈
线程状态
1:初始状态,线程刚创建,线程不参与调度 RT_THREAD_INIT
2:就绪状态,线程按照优先级排队,等待执行 RT_THREAD_READY
3:运行状态,系统正在运行 RT_THREAD_RUNNING
4:挂起状态,阻塞状态,资源不可以或者主动延时导致系统挂起,不参与线程调度 RT_THREAD_SUSPEND
线程优先级
表示线程被调用的优先程度,线程越重要,优先级越高,越能被调用,rtt最大支持256个线程优先级,0的优先级最高,在ARM Cortex -M系列中一般是32个优先级,空闲线程的优先级最低,用户一般不使用空闲线程
时间片
每个线程都有时间片这个参数,时间片仅仅对相同优先级的线程有效,如果两个线程的优先级相同,时间片分别为10和5,那么系统会在这两个线程间来回执行,一个执行10个时钟节拍,一个执行5个时钟节拍

创建线程

创建动态线程:

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)

这个函数的类型是线程控制块结构体,同时接收该函数的返回值也要新建一个线程控制块来接收,线程创建未成功返回值是RT_NULL,创建成功返回thread

rt_thread_t tid;         //新建一个线程控制块tid
/* 创建线程1 */
tid = rt_thread_create("thread1",
                       thread1_entry, RT_NULL,
                       THREAD_STACK_SIZE,
                       THREAD_PRIORITY,
                       THREAD_TIMESLICE);
if (tid != RT_NULL)       //线程创建成功,启动线程
    rt_thread_startup(tid);

线程的删除调用函数rt_err_t rt_thread_delete(rt_thread_t thread),线程删除成功返回RT_EOK,线程删除失败返回-RT_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)

静态线程的线程句柄(或者说线程控制块指针)、线程栈由用户提供。静态线程是指线程控制块、线程运行栈一般都设置为全局变量,在编译时就被确定、被分配处理,内核不负责动态分配内存空间。需要注意的是,用户提供的栈首地址需做系统对齐(例如 ARM 上需要做 4 字节对齐)。
返回RT_EOK线程创建成功,-RT_ERROR,线程创建失败
线程脱离函数rt_err_t rt_thread_detach (rt_thread_t thread);
线程句柄与静态线程创建的线程句柄相同

启动线程

rt_err_t rt_thread_startup(rt_thread_t thread);

函数参数为线程句柄,返回值为RT_EOK,-RT_ERROR,
启动线程是将函数从初始化/创建的线程从初始状态更改为就绪状态,如果有更高优先级的线程被启动,那么就优先启动高优先级的线程

临界区
多个线程操作,访问同一块区域的代码,这块代码被称为临界区

线程同步的核心思想:在访问临界区的时候,只允许一类或者一个线程运行

线程同步方式:信号量,互斥量,时间集

信号量控制块

struct rt_semaphore
{
   struct rt_ipc_object parent;  /* 继承自 ipc_object 类 */
   rt_uint16_t value;            /* 信号量的值 */
};
/* rt_sem_t 是指向 semaphore 结构体的指针类型 */
typedef struct rt_semaphore* rt_sem_t;

信号量的管理方式
创建动态信号量:rt_sem_create
删除动态信号量:rt_sem_delete
初始化静态信号量:rt_sem_init
脱离静态信号量:rt_err_t rt_sem_detach(rt_sem_t sem);
获取信号量:rt_err_t rt_sem_take (rt_sem_t sem, rt_int32_t time);
无等待获取信号量:rt_err_t rt_sem_trytake(rt_sem_t sem);
释放信号量;rt_err_t rt_sem_release(rt_sem_t sem);

二值信号量
信号量资源被获取了,信号量的值就是0,信号量的资源被释放了,信号量的值就是1,把这种只有0和1两种情况的信号量称为二值信号量。
二值信号量的使用:
当没有数据到来的时候,线程就进入阻塞态,不参与线程调度,等到数据来了,释放一个二值信号量,线程立即从阻塞态中删除,进入就绪态,让后运行的时候处理数据,这样系统的资源被很好的利用。
二值信号量在使用完毕需要及时释放

计数信号量:
信号量也可以认为是一个递增或递减的计数器,需要注意的是信号量的值非负。例如:初始化一个信号量的值为 5,则这个信号量可最大连续减少 5 次,直到计数器减为 0。资源计数适合于线程间工作处理速度不匹配的场合,这个时候信号量可以做为前一线程工作完成个数的计数,而当调度到后一线程时,它也可以以一种连续的方式一次处理多个事件。例如,生产者与消费者问题中,生产者可以对信号量进行多次释放,而后消费者被调度到时能够一次处理多个信号量资源。
类似于生活中停车的实例:有五个停车位,每次释放一个停车位也就是一个信号量,释放完的停车位被停车,也就是信号量被获取,最多可以释放五个停车位,之后就要等待被获取的信号量释放,也就是把车开走才可以继续释放停车位。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值