• 嵌入式实时操作系统的设计与开发(aCoral线程学习)


    真正的RTOS,基本上没有做到进程,只是停留在多线程,因为多进程要解决很多问题,且需要硬件支持,这样就使得系统复杂了,从而就可能影响系统实时性。

    线程之间是共享地址的,也就是说当前线程的地址相对于其它线程的地址是可见的,如果修改了地址的内容,其它线程是可以知道,并且能访问的。

    int i = 1;
    test()
    {
    	sleep(10);
    	printf("%d",i);
    }
    
    int main()
    {
    	create_task(test,...);
    	i++;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    如果create_task对应的是创建线程的接口,则test输出2.
    如果是创建进程的接口,则test输出1.
    如果是多进程,main函数所在进程和test所在进程是不能相互访问彼此之间的变量的。

    1. 地址保护。每个进程都有自己的地址空间,如果当前进程跨界访问到了其它进程的区域,则会出错,就访问不了这个地址,这种地址保护需要硬件有存储器保护单元MPU(Memory Protectin Unit)的支持。
    2. 虚拟地址。各个进程仅管都是访问同一地址,但是由于虚拟地址隐射,他们对应的物理地址不一样,所以读取的值就会不一样。这种虚拟地址需要有内存管理单元MMU(Memory Managment Unit)的支持。

    进程之间相互独立、隔离,一个进程的崩溃或错误操作不会影响其它进程。但无法直接访问全局变量,因为全局变量变成了进程范围内的全局变量了,这样进程之间的共享和通信就变得困难。

    描述线程

    线程就是一段代码的执行体。
    线程保护了“执行代码+执行环境”,执行环境就是“堆栈+寄存器”。
    线程控制块(TCB)是acoral_thread_t。

    typedef struct{
    	acoral_res_t res; //线程控制块是一种资源
    	unsigned char state;
    	...
    }acoral_thread_t;
    
    • 1
    • 2
    • 3
    • 4
    • 5

    ACORAL_THREAD_EXIT为退出状态,意味着某个线程退出了,也就是说不会再参与调度,但此时该线程的资源,如线程控制块TCB、堆栈等资源还未释放,而RELEASE状态意味着可以释放这些资源。

    • CPU:指示线程在哪个CPU上运行,当前不支持线程迁移,也就是说,线程创建时在哪个CPU上,以后的整个执行过程也是在该CPU上。
    • acoral_list_t ready;
      acoral_list_t timeout;
      acoral_list_t waiting;
      acoral_list_t global_list;
    • stack:表示线程的堆栈。在当前线程被其它线程抢占,并在切换到其它线程的时候,当前线程的stack会赋值为CPU堆栈寄存器sp的值。每个线程都有自己的堆栈,用以存放自己的运行环境。
    • stack_buttom:这时栈底,一个线程的堆栈是有大小的,所以就有个栈底,当堆栈指针超过了栈底,是会出问题的。这时sp指向的内存地址已经不是本线程自己的内存空间,这样可能会破坏了其它线程的数据结构,严重时会导致系统崩溃。
    • stack_size:堆栈大小
    • delay:当用户需要延迟某个线程的执行时,用它来指定延迟的时间,单位是Ticks,当用户调用acoral_delay_self()时传入的时间参数转化为Tick再赋给delay成员。
    • private_deta:长久备用数据指针,用于线程策略私有数据指针。

    res

    typedef union{
       int id;
       int next_id;
    }acoral_res_t;
    
    • 1
    • 2
    • 3
    • 4

    由于线程控制块是一种资源,id表示线程的资源ID。
    当某个资源空闲时,id的高16位表示该资源在资源池的编号,分配后表示该资源的ID。
    next_id表示下一资源的ID,它是个空闲链表指针,指向下一个空闲的资源的编号,属于资源ID的一部分。
    总之,res代表了线程的ID。
    资源ID由资源类型Type和空闲内存池两部分组成。

    采用了资源池的内存管理方式,资源池由结构acoral_pool_t定义。
    若要创建某一新线程,将调用函数acoral_get_free_pool(),从空闲资源池中获取一空闲内存,并获取其ID号。将申请到的内存空间供该线程使用。

    typedef struct {
       void *base_adr; //这有两个作用,在为空闲的时候,它指向下一个pool,否则为它管理的资源的基地址
       void *res_free; //指向下一空闲资源
       int id;
       unsigned int size;
       unsigned int num;
       unsigned int position;
       unsigned int free_num;
       acoral_pool_ctrl_t *ctrl;
       acoral_list_t ctrl_list;
       acoral_list_t free_list;
    }acoral_pool_t;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    空闲内存池ID由内存管理模块在初始化分配内存时,根据当前块数而定。

    void acoral_res_sys_init()
    {
    	acoral_pool_t *pool;
    	unsigned int i;
    	pool = &acoral_pools[0];
    	for(i=0; i<(ACORAL_MAX_POOLS-1); i++)
    	{
    		pool->base_adr = (void *)&acoral_pools[i+1]; //初始化时所有pool为空闲,故成员base_adr指向下一个pool
    		pool->id = i;
    		pool++;
    	}
    	pool->base_adr = (void *)0;
    	acoral_free_res_pool = &acoral_pools[0];//空闲资源池指针
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    //创建资源内存池
    unsigned int acoral_create_pool(acoral_pool_ctr_t *pool_ctrl)
    {
    	acoral_pool_t *pool;
    	if(pool_ctr->num >= pool_ctrl->max_pools)
    	{
    		return ACORAL_RES_MAX_POOL;
    	}
    	pool = acoral_get_free_pool();
    	if(pool == NULL)
    		return ACORAL_RES_NO_POOL;
    	pool->id = pool_ctrl->type << ACORAL_RES_TYPE_BIT | pool->id;
    	pool->size = pool_ctrl->size;
    	pool->num = pool_ctrl->num_per_pool;
    	pool->base_adr = (void *)acoral_malloc(pool_size * pool->num);
    	if(pool->base_adr == NULL)
    		return ACORAL_RES_NO_MEM;
    	pool->res_free = pool->base_adr;
    	pool->free_num = pool->num;
    	pool->ctrl = pool_ctrl;
    	acoral_pool_res_init(pool);
    	acoral_list_add2_tail(&pool->ctrl_list, pool_ctrl->pools);
    	acoral_list_add2_tail(&pool->free_list, pool_ctrl->free_pools);
    	pool_ctrl->num++;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    Prio

    #define CFG_MAX_THREAD (40)
    #definne ACORAL_MAX_PRIO_NUM ((CFG_MAX_THREAD +1) & 0xff) //41,总共有40个线程,有0~40共41个优先级
    #define ACORAL_MINI_PRIO CFG_MAX_THREAD //最低优先级40
    
    typedef enum{
    	ACORAL_INIT_PRIO, //init线程独有的0优先级
    	ACORAL_MAX_POOL, //系统允许的最高优先级
    	ACORAL_HARD_RT_PRIO_MAX, //硬实时任务最高优先级
    	ACORAL_HARD_RT_PRIO_MIN = ACORAL_HARD_RT_PRIO_MAX+CFG_HARD_RT_PRIO_NUM,
    	ACORAL_NOHARD_RT_PRIO_MAX, //非硬实时任务最高优先级
    
    	ACORAL_DAEMON_PRIO = ACORAL_MINI_PRIO-2,
    	ACORAL_NOHARD_RT_PRIO_MIN; //非硬实时任务最低优先级
    	ACORAL_IDLE_PRIO;
    }PrioEnum;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    优先级与数字成反比,数字越大,优先级越低。
    aCoral的初始优先级为0,最高优先级是1,最小优先级是总的优先级数减1。

    #define CFG_MEM2 1 //任意大小内存分配系统是否启用
    #define CFG_MEM2_SIZE (1024000) //任意大小内存分配系统的大小,是从伙伴系统管理的内存中拿出一部分
    #define CFG_MIN_STACK_SIZE (512) //线程最小拥有的字节数
    
    • 1
    • 2
    • 3

    双向链表

    acoral_list_t ready; //用于挂载全局就绪队列
    acoral_list_t timeout; //超时阻塞
    acoral_list_t waiting; //延时
    acoral_list_t global_list; //全局线程列表
    
    • 1
    • 2
    • 3
    • 4
    struct acoral_list{
    	struct acoral_list *next,*prev;
    };
    typedef struct acoral_list acoral_list_t;
    
    • 1
    • 2
    • 3
    • 4

    这4个acoral_list_t成员用来将线程结构挂到相应链表队列上。
    在这里插入图片描述
    以就绪队列ready为例,当用户调用了acoral_rdy_thread或acoral_resume_thread接口时,就会将线程挂到就绪队列acoral_ready_queue上,这就是将就绪队列ready成员挂到这个链表上。

    挂到相应链表队列上的方式与Linux类似,这种方式的优点是:可以用相同的数据处理方式来描述所有双向链表,不用再单独为各个链表编写各种函数。

    线程优先级

    aCoral是一个支持多核的RTOS,因此,在开发初期就得考虑线程数量的问题。

    aCoral的就绪队列采用的是优先级链表,每个优先级是一个链表,相同的优先级的线程都挂在该链表上。

    对于RTOS而言,几乎都是采用了基于优先级的抢占调度策略。

    typedef struct{
    	unsigned int num; //就绪的线程数
    	unsigned int bitmap[PRIO_BITMAP_SIZE];//优先级位图,每一位对应一个优先级,为1表示这个优先级有就绪线程
    	acoral_list_t queue[ACORAL_MAX_PRIO_NUM]; //每一个优先级都有独立的队列
    }acoral_rdy_queue_t;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • num:定义了就绪任务的总数
    • bitmap[PRIO_BITMAP_SIZE]:用来标识某一优先级是否有就绪队列,这样才能确保以O(1)复杂度找出最高优先级的线程。
    #define PRIO_BITMAP_SIZE ((ACORAL_MAX_PRIO_NUM+31)/32)
    
    • 1

    就绪队列中的优先级位图的大小,目前等于2,优先级数目除以32向上取整。

    每个变量从右到左每一位依次代表一个优先级。

    aCoral采用私有就绪队列,也就是每个CPU有一个就绪队列。

  • 相关阅读:
    机器学习-聚类算法
    mysql高级刷题-01-求中位数
    【21t天算法挑战赛】排序算法——直接选择排序
    【Mac】鼠标控制\移动\调整窗口大小BBT|边缘触发调整音量\切换桌面
    22/6/26
    新版WordPress插件短视频去水印小程序源码
    使tkinter开发GUI程序2 -- 窗口组件配置管理Layout Management
    记录:移动设备软件开发(activity组件)
    DOM——事件语法
    车载语音交互「停摆」
  • 原文地址:https://blog.csdn.net/Caramel_biscuit/article/details/133852175