|
开篇先说个题外话前听取了jorya_txj的意见,有些东西的验证可以采取printf的方法,并不要都用硬件仿真。
于是楼主在F4上开启了一路DMA UART
经测试效果良好
于是初步决定和内核内部测试有关的,使用硬件仿真的方法,和内部应用有关的测试,采用UART的方法。
OK,开始这一次的任务,介绍一下硬件和软件的环境:硬件:STM32F4,软件:上一节“任务管理”中的已经创建了3个任务的工程.
我将任务分解为3步
就绪队列的数据结构:
typedef struct RAW_RUN_QUEUE {
RAW_U8 highest_priority;
LIST task_ready_list[CONFIG_RAW_PRIO_MAX];
RAW_U32 task_bit_map[NUM_WORDS];
} RAW_RUN_QUEUE;
首先,对RAW_RUN_QUEUE 进行搜索,可以看到这个结构体只定义了一个变量 raw_ready_queue,想必它就维护者整个系统的就绪队列。
void test_task1(void * pParam)
{
RAW_U32 * Res;
while(1)
{
Res = (raw_get_task_user_point(&test_task_obj1, 0));
if((*Res) == 1)
{
raw_sleep(50);
}
UartSendNum("TASK1:now the highest_priority is\n",raw_ready_queue.highest_priority);
raw_sleep(50);
if(Switch)
{
space = raw_get_system_global_space();
}
raw_task_stack_check(&test_task_obj1, &StackSpace);
}
}
void test_task2(void * pParam)
{
RAW_U32 * Res;
RAW_U32 Task2Tmp = 2;
while(1)
{
raw_sleep(5);
UartSendNum("TASK2:now the highest_priority is\n",raw_ready_queue.highest_priority);
raw_sleep(40);
}
}
void test_task3(void * pParam)
{
RAW_U32 * Res;
while(1)
{
UartSendNum("TASK3:now the highest_priority is\n",raw_ready_queue.highest_priority);
raw_sleep(30);
}
}
在三个任务中都调用raw_sleep使任务等待时间信号量而阻塞,从而调度优先级的任务。再用串口发上当前最最高优先级。
经检验,这个优先级指的是当前等待序列中的任务中的最高优先级
结构中的第二项 LIST task_ready_list[CONFIG_RAW_PRIO_MAX];
CONFIG_RAW_PRIO_MAX是指当前系统最多支持多少个优先级,可见,为每一个优先级准备了一个链表。
结构中的第二项 LIST RAW_U32 task_bit_map[NUM_WORDS];
raw os 找到最高优先级的方法,实质上去查找task_bit_map第一个bit为1的位置。
楼主找到了它的定义处 #define NUM_WORDS ((CONFIG_RAW_PRIO_MAX + 31) / 32) 起初不理解,后来恍然大悟,每一个链表都需要一个链表头。
任务链表的数据结构:
typedef struct LIST {
struct LIST *next;
struct LIST *previous;
} LIST;
可见,RAW OS 中链表的结构是双向的。
研究了一番RAW OS 的这个链表的作用
首先,在进入OS后,就绪队列开始起作用,现在有任务优先级分别为11 12 13 的三个任务。
可以发现,就绪队列中的task_ready_list,除了11,12,13,其他任务的LIST 链表头next 和 previous 指向的都是本身。
楼主在思考这个LIST和任务怎么对接的时候翻到了任务控制的结构体。
可以看到,每一个任务控制结构体重都有个task_list 这样的画,对接就可行了,想必是这样的。
最后是相关内核函数:
这里复制一下jorya_txj的原话
1 void add_ready_list_head(RAW_RUN_QUEUE *rq, RAW_TASK_OBJ *task_ptr)
函数功能:
往就绪队列rq里面加入一个任务。也就是说该任务已经处于就绪状态。需要注意的是加入的任务加到就绪队列头部去。
此函数的参数有4个,含义如下:
rq 为指针指向raw os 系统维护的一个优先级队列实体raw_ready_queue。
task_ptr是要准备被加入的任务。
函数的返回值:
无
2 void add_ready_list_end(RAW_RUN_QUEUE *rq, RAW_TASK_OBJ *task_ptr)
函数功能:
往就绪队列rq里面加入一个任务。也就是说该任务已经处于就绪状态。需要注意的是加入的任务加到就绪队列尾部去。
此函数的参数有4个,含义如下:
rq 为指针指向raw os 系统维护的一个优先级队列实体raw_ready_queue。
task_ptr是要准备被加入的任务。
函数的返回值:
无
3 void remove_ready_list(RAW_RUN_QUEUE *rq, RAW_TASK_OBJ *task_ptr)
函数功能:
往就绪队列rq里面移除一个任务。也就是说该任务已经处于非就绪状态。非就绪状态有很多任务状态,具体请参阅任务状态一章。需要注意的是此函数内部可能会更新系统的rq->highest_priority, rq->highest_priority永远维护着整个系统最高的优先级。
此函数的参数有2个,含义如下:
rq 为指针指向raw os 系统维护的一个优先级队列实体raw_ready_queue。
task_ptr是要准备被从就绪态移除出去的任务。
函数的返回值:
无
4 void move_to_ready_list_end(RAW_RUN_QUEUE *rq, RAW_TASK_OBJ *task_ptr)
函数功能:
把任务task_ptr放到就绪队列最后面去。
此函数的参数有2个,含义如下:
rq 为指针指向raw os 系统维护的一个优先级队列实体raw_ready_queue。
task_ptr是要准备放到就绪队列最后面去的任务。
函数的返回值:
无
5 void get_ready_task(RAW_RUN_QUEUE *rq)
函数功能:
更新就绪队列里面优先级最高的任务, 此函数的算法效率相当重要,对实时性有举足轻重的影响,此函数必须要运行时间是恒定的,因为此函数是在关了系统中断情况下使用的需要越快越好,raw os的实现只有3句C语言,具体的请参照相关代码。
此函数的参数有1个,含义如下:
rq为指针指向raw os系统维护的一个优先级队列实体raw_ready_queue。
前4个函数可顾名思义,重点研究get_ready_task函数,因为这个函数在临界区内实现,所以它的速度关乎系统的响应性。
代码如下
void get_ready_task(RAW_RUN_QUEUE *rq)
{
LIST *node;
RAW_U8 highest_pri;
if (task_0_events) {
high_ready_obj = &raw_task_0_obj;
return;
}
highest_pri = rq->highest_priority;
node = rq->task_ready_list[highest_pri].next;
high_ready_obj = list_entry(node, RAW_TASK_OBJ, task_list);
}
list_entry是一个宏定义
#define list_entry(node, type, member) ((type *)((RAW_U8 *)(node) - (RAW_U32)(&((type *)0)->member)))
通过这句话,(RAW_U8 *)(node)取出最高优先级等待序列中第一个任务tasklist的偏移地址,再减去 (RAW_U32)(&((type *)0)->member)即tasklist 相对于一个结构的偏移地址。取出一个任务控制模块结构的地址。
楼主经仿真,确实如此。图就不贴啦 :)
于是,“就绪队列”结束。
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?注册
x
阿莫论坛20周年了!感谢大家的支持与爱护!!
一只鸟敢站在脆弱的枝条上歇脚,它依仗的不是枝条不会断,而是自己有翅膀,会飞。
|