——基于[野火®]《RT-Thread 内核实现与应用开发实战—基于STM32》第一部分的学习,6-12章,是为mdk仿真,了解了RT-Thread的基础。感谢野火。可能理解有不对的地方请大神指教。
1.给出结构体的成员,反推结构体的起始地址。
这个算法是把数字0强制转换成这个结构体类型,然后取出这个结构体成员,这就是这个结构体成员的在结构体中偏移量。有了这个偏移量再根据这个成员的地址,就算出了结构体的地址:结构体成员地址-其在结构体中的偏移量。
2.就绪表中最高优先级线程的快速查询算法——位图
这个厉害了,用空间换时间。叫位图意思可能是建立一张查找最高优先级的图表的意思吧。
先建立个线程优先级就绪与否的标志:就绪优先级组。其就是个整型变量或数组。整型的每一位对应就绪表中的下标也就是优先级。位0代表优先级0,依次类推。当然支持的优先级超过32了,就需要整型数组,每个数组成员的每位都代表优先级,那么这个数组的第1个成员代表优先级0-31,第2个成员代表优先级32-63,等等依次类推。
它是如何找到最高优先级的呢?它的映射是从低位到高位的优先级。根据这个特性又建了一张表,无符号char类型的数组,一共256个成员即占用256个字节。它是怎么空间换时间的呢?因为这256个成员代表了8位排列的所有数值对应的最高优先级位是哪个。就是用最笨的方法,这8位有0-255个数值,共256个排列方式,把这个所有排列的最高优先级位,人手工的给它找出来再放到表格里。比如0x01,把它当成下标取出表格里的值,它直接给出0,因为是位0是置1了,也就是最高优先级是0的优先级已经置1就绪了,所以要切换到这个最高优先级的线程来执行。比如0x0A,直接把它当成这个表格的下标来取出值就是:1。等等。以此以空间换取时间。
空间就是:表格、整型变量或数组。
时间:减少了循环查询最高优先级的时间。
3.RTT插入系统定时器列表升序算法
3.1
/* 在双向链表根节点后面插入一个节点 */
2 rt_inline void rt_list_insert_after(rt_list_t *l, rt_list_t *n)
3 {
4 l->next->prev = n; /* 第 ① 步 */
5 n->next = l->next; /* 第 ② 步 */
6
7 l->next = n; /* 第 ③ 步 */
8 n->prev = l; /* 第 ④ 步 */
9 }
这个其实是:在节点l和l->next之间插入一个节点n
步骤记忆:后指我,我指后;前指我,我指前。这里面的后是指原来的l->next。我是指n。前指l。
这个函数其实是:输入一个节点l,和新的节点n,那就实现在这l后面、原l->next的前面插入n,新的链表结构为:根节点 l n 原l->next ……
3.2按照升序排列将各线程的定时器插入到系统定时器列表中
/* 因为 RT_TIMER_SKIP_LIST_LEVEL 等于 1,这个循环只会执行一次 */
47 for (row_lvl = 0; row_lvl < RT_TIMER_SKIP_LIST_LEVEL; row_lvl++) (5)
48 {
49 /* 当系统定时器列表 rt_timer_list 为空时,该循环不执行 */ (6)
50 for (; row_head[row_lvl] != timer_list[row_lvl].prev; row_head[row_lvl] = row_head[row_lvl]->next)
51 {
52 struct rt_timer *t;
53
54 /* 获取定时器列表节点地址 */
55 rt_list_t *p = row_head[row_lvl]->next; (6)-①
56
57 /* 根据节点地址获取父结构的指针 */ (6)-②
58 t = rt_list_entry(p, /* 节点地址 */
59 struct rt_timer, /* 节点所在父结构的数据类型 */
60 row[row_lvl]); /* 节点在父结构中叫什么,即名字 */
61
62 /* 两个定时器的超时时间相同,则继续在定时器列表中寻找下一个节点 */
63 if ((t->timeout_tick - timer->timeout_tick) == 0) (6)-③
64 {
65 continue;
66 }
67 /* */
68 else if ((t->timeout_tick - timer->timeout_tick) < RT_TICK_MAX / 2)
69 {
70 break;
71 }
72
73 }
74 /* 条件不会成真,不会被执行 */
75 if (row_lvl != RT_TIMER_SKIP_LIST_LEVEL - 1)
76 {
77 row_head[row_lvl + 1] = row_head[row_lvl] + 1;
78 }
其中,决定其排序方向的方法之一是:
62 /* 两个定时器的超时时间相同,则继续在定时器列表中寻找下一个节点 */
63 if ((t->timeout_tick - timer->timeout_tick) == 0) (6)-③
64 {
65 continue;
66 }
67 /* */
68 else if ((t->timeout_tick - timer->timeout_tick) < RT_TICK_MAX / 2)
69 {
70 break;
71 }
这是升序插入的关键点、转折点,你看当相等时前面的保存根节点的数组就继续下移,仿真看看,再结合野火书看看。如果要插入的定时器的实际超时节拍数小于这个根节点的下个节点的定时器,那么就break,然后插入这个节点,传入的l参数是根节点,于是就在根节点和根节点的下个节点之间插入了这个新节点。
注意,上面的比较:插入的节点插入的位置是:根节点和根节点的下一个节点之间,它比较的是根节点的下一个节点。如果比它小,那就采用rt_list_insert_after在根节点和根节点的下一个节点(也就是它比较的节点)之间。比它大,那就更新根节点为刚才比较的那个节点也就是原根节点的下一个节点的地址。然后让他做根节点,然后让插入的节点继续和根节点的下一个节点进行比较,比如大就继续让根节点往后移。依次循环而实现升序排列。
如果想要降序排列,就是由大到小排列,我想到这个方法:
62 /* 降序排列 */
63 if ((t->timeout_tick - timer->timeout_tick) == 0) (6)-③
64 {
65 break;
66 }
67 /* */
68 else if ((t->timeout_tick - timer->timeout_tick) < RT_TICK_MAX / 2)
69 {
70 continue;
71 }
72 else
73 {
74 break;
75 }
这个从逻辑分析是对的,你看,当插入时比较延时相等或者要插入的实际到时时间比根节点的下一个节点大,就break,于是在根节点和其下一个节点之间插入这个大的节点。当插入的节点实际时间比根节点的下个节点小,就continue更新根节点为下个节点,然后进行再比较。这样就实现了降序——从大到小的排序。
注意:根节点只是参考,随着循环它在变的。row_head[row_lvl] = row_head[row_lvl]->next