用户创建一个线程时须指定用户希望采用的调度策略。
//周期策略数据控制块
typedef struct{
unsigned char prio;
unsigned char prio_type;
unsigned int time;
}acoral_period_policy_data_t;
普通线程是指用户需要用通用调度策略进行调度的线程,例如,用户希望自己创建的线程采用FIFS的方式进行调度。
int acoral_create_thread(void (*route)(void *args), unsigned int stack_size, void *args, char *name,void *stack, unsigned int sched_policy, void *data){
acoral_thread_t *thread;
thread = acoral_alloc_thread();
if(NULL==thread){
acoral_print("Alloc thread:%s fail\n",name);
acoral_print("No Mem Space or Beyond the max thread\n");
return -1;
}
thread->name = name;
stack_size = stack_size & (~3);
thread->stack_size = stack_size;
if(stack != NULL)
thread->stack_buttom = (unsigned int *)stack;
else
thread->stack_buttom = NULL;
thread->policy = sched_policy;
return acoral_policy_thread_init(sched_policy,thread,route,args,data);
}
stack_size = stack_size & (~3);
这个代码通常用于将一个整数stack_size舍入到最接近的较小的能被4整除的值。
这可能出现在与线程或内存分配相关的代码中,以确保栈大小或内存块的大小是4的倍数。
创建线程需要做的第一项工作是为该线程分配内存空间,线程是通过tcb描述的,为线程分配内存空间就是为TCB分配空间,其返回值是刚分配的TCB的指针。
acoral_thread_t *acoral_alloc_thread(){
return (acoral_thread_t *)acoral_get_res(&acoral_thread_pool_ctrl);
}
资源池控制块
typedef struct{
unsigned int type;//资源类型
unsigned int size;//资源大小,一般就是结构体的大小,如线程控制块的大小,用sizeof(acoral_thread_t)这种形式赋值
unsigned int num_per_pool; //每个资源池对象的数目。
unsigned int num; //已经分配的资源池的个数
unsigned int max_pools; //最多可以分配多少个资源池
acoral_list_t *free_pools;
acoral_list_t *pools,list[2];
unsigned char *name;
}acoral_pool_ctrl_t;
资源池管理的资源内存是从第一级内存系统(伙伴系统)分配的,为了最大限度使用内存,减少内存碎片,对象的个数、最大值、可分配内存等都是通过计算后由用户指定的。
例如,伙伴算法设定基本内存块的大小为1KB,资源的大小为1KB,用户一个资源池包含20个资源,这样计算下来要分配20KB的空间,而由于伙伴系统只能分配2i个基本内存块的大小,故会分配32KB,32KB包含32个资源对象,故每个资源池的对象的个数更改为32。
acoral_res_t *acoral_get_res(acoral_pool_ctrl_t *pool_ctrl)
{
acoral_list_t *first;
acoral_res_t *res;
acoral_pool_t *pool;
acoral_enter_critical();
first = pool_ctrl->free_pools->next;
if(acoral_list_empty(first))
{
}
}
int comm_policy_thread_init(acoral_thread_t *thread, void (*route)(void *args), void *args, void *data){
unsigned int prio;
acoral_comm_policy_data_t *policy_data;
policy_data = (acoral_comm_policy_data_t *)data;
prio = policy_data->prio;
if(policy_data->prio_type == ACORAL_NOHARD_PRIO)
{
prio += ACORAL_NOHARD_RT_PRIO_MAX;
if(prio >= ACORAL_NOHRAD_RT_PRIO_MIN)
prio = ACORAL_NOHRAD_RT_PRIO_MIN;
}
thread->prio = prio;
if(acoral_thread_init(thread, route, acoral_thread_exit, args) != 0)
{
acoral_print("No comm thread stack:%s\r\n",thread->name);
acoral_enter_critical();
acoral_release_res((acoral_res_t *)thread);
acoral_exit_critical();
return -1;
}
acoral_resume_thread(thread);
return thread->res.id;
}
unsigned int acoral_thread_init(acoral_thread_t *thread, void (*route)(void *args), void (*exit)(void), void *args)
{
unsigned int stack_size = thread->stack_size;
if(thread->stack_buttom == NULL) //判断堆栈指针是否为NULL,如果为NULL,则需要动态分配
{
if(stack_size<CFG_MIN_STACK_SIZE)
stack_size=CFG_MIN_STACK_SIZE;
thread->stack_buttom = (unsigned int *)acoral_malloc(stack_size);
if(thread->stack_buttom == NULL)
return ACORAL_ERR_THREAD_NO_STACK;
thread->stack_size = stack_size;
}
thread->stack = (unsigned int *)((char *)thread->stack_buttom_stack_size-4);
HAL_STACK_INIT(&thread->stack, route, exit, args); //模拟线程创建时的堆栈环境
thread->data = NULL;
thread->state = SUSPEND;
acoral_init_list(&thread->waiting);
acoral_init_list(&thread->ready);
acoral_init_list(&thread->timeout);
acoral_init_list(&thread->global_list);
acoral_enter_critical();
acoral_list_add2_tail(&thread->global_list, &acoral_threads_queue);
acoral_exit_critical();
return 0;
}
#define HAL_STACK_INIT(stack,route,exit,args) hal_stack_init(stack, route, exit, args);
HAL_STACK_INIT是与硬件相关的函数,不同的处理器有不同的寄存器[寄存器个数、寄存器功能分配(程序指针、程序当前状态寄存器)、连接寄存器、通用寄存器等],这些寄存器体现了当前线程的运行环境**,如果当前线程被其中断或线程所抢占**,将会发生上下文切换。
HAL_STACK_INIT()就是用来规定寄存器保存顺序的。
ARM9 S3C2410的线程环境是通过R0~R15及CPSR来保存的,即发生上下文切换时,要保存这16个寄存器的值(除R13(SP)外)。
故在堆栈初始化时就得压入这么多寄存器来模拟线程的环境(以方便在不知道具体针对某一硬件平台的时候,模拟堆栈的压栈),未来方便修改和操作,用一个数据结构表示环境。
typedef struct{
unsigned int primask;
unsigned int r4; ///<通用寄存器
unsigned int r5; ///<通用寄存器
unsigned int r6; ///<通用寄存器
unsigned int r7; ///<通用寄存器
unsigned int r8; ///<通用寄存器
unsigned int r9; ///<通用寄存器
unsigned int r10; ///<通用寄存器
unsigned int r11; ///<通用寄存器
unsigned int r0; ///<通用寄存器
unsigned int r1; ///<通用寄存器
unsigned int r2; ///<通用寄存器
unsigned int r3; ///<通用寄存器
unsigned int r12; ///<通用寄存器
unsigned int lr; ///<链接寄存器
unsigned int pc; ///<程序计数器
unsigned int cpsr;
}hal_ctx_t;
由于是用C语言来模拟线程创建时的堆栈环境,所以用宏转换定义hal_stack_init()来实现HAL_STACK_INIT()。
其中,R0R7是通用寄存器,R8R12是影子寄存器,R14(LR)是链接寄存器,R15(PC)是程序指针。
void hal_stack_init(unsigned int **stk, void (*route)(), void (*exit)(), void args)
{
hal_ctx_t *ctx = (hal_ctx_t *)*stk;
ctx--; //由于堆栈是向下生长的,所以用ctx--。
ctx = (hal_ctx_t *)((unisgned int *)ctx + 1); //调整了4个字节
ctx->r0 = (unsigned int)args;
ctx->r1=0;
ctx->r2=0;
ctx->r3=0;
ctx->r4=0;
ctx->r5=0;
ctx->r6=0;
ctx->r7=0;
ctx->r8=0;
ctx->r9=0;
ctx->r10=0;
ctx->r11=0;
ctx->r12=0;
ctx->lr = (unsigned int)exit;
ctx->pc = (unsigned int)route;
ctx->cpsr = 0x01000000;
ctx->primask = 0;
*stk = (unsigned int *)ctx;
}
恢复线程,将新创建的线程挂载到一个就绪队列上。
线程恢复有两个接口:
acoral_resume_thread()和acroal_ready_thread()
前者比后者多了一个acoral_sched调度函数和一个判断。
acoral_resume_thread可由用户调用,acoral_rdy_thread用户不能直接调用,只能是内核内部调用。
acoral_resume_thread是可能立即导致当前线程挂起的调用,而rdy_thread不会,必须显示调用acoral_sched后才能挂起。
void acoral_resume_thread(acoral_thread_t *thread){
acoral_u8 CPU;
if(!(thread->state & ACORAL_THREAD_STATE_SUSPEND)) //如果线程不处于suspend状态,则不需要唤醒
return;
#ifdef CFG_CMP
CPU = thread->CPU;
if(CPU != acoral_current_CPU){
acoral_ipi_cmd_send(CPU,ACORAL_IPI_THREAD_RESUME,thread->res.id, NULL);
return;
}
#endif
HAL_ENTER_CRITICAL();
acoral_rdyqueue_add(thread);
acoral_exit_critical();
acoral_sched();
}
//将线程挂到就绪队列上
void acoral_rdyqueue_add(acoral_thread_t *thread)
{
acoral_rdy_queue_t *rdy_queue;
rdy_queue = &acoral_ready_queues;
acoral_prio_queue_add(rdy_queue, thread->prio, &thread->ready);
thread->state &= ~ACORAL_THREAD_STATE_SUSPEND;
thread->state |= ACORAL_THREAD_STATE_READY;
acoral_set_need_sched(true);
}
void acoral_prio_queue_add(acoral_rdy_queue_t *array, unsigned char prio, acoral_list_t *list)
{
acoral_list_t *queue;
acoral_list_t *head;
array->num++;
queue = array->queue + prio; //根据线程的优先级找到线程所在的优先级链表
head = queue;
acoral_list_add2_tail(list, head);
acoral_set_bit(prio, array->bitmap);
}
一个普通线程创建的最后一步是调用内核函数acoral_sched(),由内核根据调度算法安排线程执行,即从就绪队列中取出符合调度算法的线程依次执行。
调用调度程序的具体位置又被称为是一个调度点(Scheduling Point)。由于调度通常是由外部事件的中断来出发,或者周期性的时钟信号来触发,因此调度点通常处于以下位置。
普通线程创建流程