当前位置:   article > 正文

裸机程序--时间片调度

裸机程序--时间片调度

1.为什么自己写一个时间片调度

        a. 网上其实有很多成熟的时间片调度例程, 包括我最开始参加工作也是抄的网上的例程(还记得当时领导问我看明白了它的调度原理吗, 作为一个自学刚参加工作的我来说, 看懂别人的意思真的很难, 当时只能含糊其词的说看得差不多)

        b. 在我看来网上的例程是有一些问题的, 计算时间的那个函数放到定时器中递减, 随着任务的增加, 定时器定时越不准确, 违背了中断的快进快出, 不过话说回来时间片本来就是一个不准确的定时.

        c. 违背了软件的开闭原则, 每次添加任务都需要进去修改那个定义任务调度的数组.

        d. 时间为0的任务不能添加到调度中.

        e. 不能删除任务: 比如某个任务我运行了一段时间, 我根本就不会运行了, 这个时候它还是在调度, 只是我们会在内部放置一个标志位, 让它快速切出去.同时也不能在运行过程中添加任务.

2.程序设计思路

        1. 先说下如何定时, 通过一个int类型(32bit)来记录1ms时间过去了, 当定时中断产生中断依次将bit0-31置1, 然后在while(1)中检测有没有置1的bit, 如果有就将任务时间递减. 由于只用一个int类型计时, 这也是为什么程序最大只能支持你程序中, 不能死等超过32ms. 

        2. 任务的删除, 添加, 转移其实都是链表的知识, 掌握好链表就能明白了.

3.程序移植

2.1 移植超级简单, 只需要添加三个文件: os.c, os.h, list.h; 将time_cb放到1ms定时器中断

  1. #include "os.h"
  2. #include "string.h"
  3. #define MAX_SLICE_SUPPORT 0x1F /* 程序运行过程最大允许被阻塞时间, 如果大于32ms, 将会导致计时不准 */
  4. volatile static unsigned int millisecond;
  5. typedef struct
  6. {
  7. unsigned int time_que;
  8. unsigned char bit_head;
  9. unsigned char bit_tail;
  10. }bit_time_t;
  11. bit_time_t task_time = {0};
  12. /* 任务等待队列和任务就绪队列 */
  13. struct list_head list_wait = LIST_HEAD_INIT(list_wait);
  14. struct list_head list_ready = LIST_HEAD_INIT(list_ready);
  15. void add_task(task_t *task)
  16. {
  17. if(task->time_slice == 0) /* 如果时间片设置为0, 则直接挂到就绪队列 */
  18. {
  19. list_add(&task->next, &list_ready);
  20. }
  21. else /* 否则将任务挂到等待队列 */
  22. {
  23. list_add(&task->next, &list_wait);
  24. }
  25. }
  26. void delet_task_onself(task_t *task)
  27. {
  28. list_del(&task->next);
  29. }
  30. static void move_task(task_t *task, struct list_head* soure_list, struct list_head* dest_list)
  31. {
  32. if(soure_list == &list_wait) /* if the task in list_wait, then move to list_ready */
  33. {
  34. list_del(&task->next);
  35. list_add(&task->next, dest_list);
  36. }
  37. else
  38. {
  39. /* task->time_slice is not zero can move to list_wait */
  40. if(task->time_slice)
  41. {
  42. list_del(&task->next);
  43. list_add(&task->next, dest_list);
  44. }
  45. }
  46. }
  47. /* 放到1ms的定时器中断里面 */
  48. inline void time_cb()
  49. {
  50. task_time.bit_tail = millisecond & MAX_SLICE_SUPPORT;
  51. task_time.time_que |= 1 << task_time.bit_tail;
  52. millisecond++;
  53. }
  54. void run_task()
  55. {
  56. task_t *node, temp_node;
  57. /* 时间队列里面是否有时间 */
  58. if(task_time.time_que & (1 << task_time.bit_head))
  59. {
  60. /* 将延时等待队列的时间减一 */
  61. list_for_each_entry(node, &list_wait, next, task_t)
  62. {
  63. node->slice_count--;
  64. if(node->slice_count == 0) /* 如果时间减完了, 则将当前任务挂到就绪队列 */
  65. {
  66. memcpy(&temp_node, node, sizeof(task_t));
  67. node->slice_count = node->time_slice;
  68. move_task(node, &list_wait, &list_ready);
  69. node = &temp_node;
  70. }
  71. }
  72. /* 将当前bit的时间清零, 并让bit_head指向下一个位置 */
  73. task_time.time_que &= ~(1 << task_time.bit_head);
  74. task_time.bit_head++;
  75. if(task_time.bit_head == MAX_SLICE_SUPPORT)
  76. {
  77. task_time.bit_head = 0;
  78. }
  79. }
  80. /* 执行就绪队列中的任务, 并将任务重新挂到等待队列 */
  81. list_for_each_entry(node, &list_ready, next, task_t)
  82. {
  83. memcpy(&temp_node, node, sizeof(task_t));
  84. move_task(node, &list_ready, &list_wait);
  85. node->task();
  86. node = &temp_node;
  87. }
  88. }
  89. unsigned int current_time()
  90. {
  91. return millisecond;
  92. }
  93. unsigned int time_interval(unsigned int *start_time)
  94. {
  95. if(*start_time == 0)
  96. {
  97. *start_time = millisecond;
  98. }
  99. return (millisecond > *start_time) ? (millisecond - *start_time) : (0xFFFFFFFF - *start_time + millisecond);
  100. }
  1. #ifndef LIST_H
  2. #define LIST_H
  3. struct list_head {
  4. struct list_head *next, *prev;
  5. };
  6. //双链表的头初始化,next, prev指向自己
  7. #define LIST_HEAD_INIT(name) { &(name), &(name) }
  8. //通过函数初始化头
  9. static inline void INIT_LIST_HEAD(struct list_head *list)
  10. {
  11. list->next = list;
  12. list->prev = list;
  13. }
  14. //添加一个新的结点
  15. static inline void __list_add(struct list_head *new, struct list_head *prev, struct list_head *next)
  16. {
  17. next->prev = new;
  18. new->next = next;
  19. new->prev = prev;
  20. prev->next = new;
  21. }
  22. //头插法
  23. static inline void list_add(struct list_head *new, struct list_head *head)
  24. {
  25. __list_add(new, head, head->next);
  26. }
  27. //尾插法
  28. static inline void list_add_tail(struct list_head *new, struct list_head *head)
  29. {
  30. __list_add(new, head->prev, head);
  31. }
  32. //删除某个结点
  33. static inline void __list_del(struct list_head *prev, struct list_head *next)//将要删除的结点从链表中释放出来
  34. {
  35. next->prev = prev;
  36. prev->next = next;
  37. }
  38. static inline void list_del(struct list_head *entry) //这个函数才是最后的删除函数
  39. {
  40. __list_del(entry->prev, entry->next);
  41. entry->next = (void *)0;
  42. entry->prev = (void *)0;
  43. }
  44. //判断结点是否为空
  45. static inline int list_empty(const struct list_head *head)
  46. {
  47. return head->next == head;
  48. }
  49. //已知结构体中的某个成员的地址ptr,得到结构体的地址
  50. #define list_entry(ptr, type, member) \
  51. ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
  52. //遍历链表, pos为链表结点, head为链表头, member为链表中的成员, type为结点类型
  53. #define list_for_each_entry(pos, head, member, type) \
  54. for (pos = list_entry((head)->next, type, member); \
  55. &pos->member != (head); \
  56. pos = list_entry(pos->member.next, type, member))
  57. #endif
  1. #ifndef OS_H
  2. #define OS_H
  3. #include "list.h"
  4. typedef struct
  5. {
  6. void (*task)();
  7. unsigned short time_slice;
  8. unsigned short slice_count;
  9. struct list_head next;
  10. }task_t;
  11. void add_task(task_t *task);
  12. void delet_task_onself(task_t *task);
  13. void run_task(void);
  14. void time_cb(void);
  15. unsigned int current_time(void);
  16. unsigned int time_interval(unsigned int *start_time);
  17. #endif

2.2 添加任务和调用

我使用了编译器特性, 自动运行程序, 这样就不需要在main函数开头手动调用函数add_task()了

  1. #include "./UART/uart.h"
  2. #include "./BaseTime/basetime.h"
  3. #include "os.h"
  4. static void task1(void);
  5. static task_t task_1 = {
  6. .task = task1,
  7. .time_slice = 500,
  8. .slice_count = 500,
  9. };
  10. static void task1()
  11. {
  12. printf("task1\n");
  13. }
  14. /* 使用编译器特性, 自动运行该程序 */
  15. __attribute__((constructor)) static void task1_add()
  16. {
  17. add_task(&task_1);
  18. }
  19. void task2();
  20. task_t task_2 = {
  21. .task = task2,
  22. .time_slice = 387,
  23. .slice_count = 387,
  24. };
  25. void task2()
  26. {
  27. static int count = 0;
  28. printf("task2ddasdfasfsafafasdsfsfsfsfsfsew\r\n");
  29. if(++count > 5)
  30. {
  31. delet_task_onself(&task_2);
  32. }
  33. }
  34. __attribute__((constructor)) void task2_add()
  35. {
  36. add_task(&task_2);
  37. }
  38. void task3()
  39. {
  40. printf("task3\r\n");
  41. }
  42. task_t task_3 = {
  43. .task = task3,
  44. .time_slice = 632,
  45. .slice_count = 632,
  46. };
  47. __attribute__((constructor)) void task3_add()
  48. {
  49. add_task(&task_3);
  50. }
  51. int main(void)
  52. {
  53. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  54. uart_init(115200);
  55. bsTime_Init(1004, 80);//1ms中断
  56. while(1)
  57. {
  58. run_task();
  59. }
  60. }

3.注意点

1.为了尽可能的节约内存, 以及程序调用的及时性, 程序运行过程最大可以等待32ms去轮询时间递减. 如果内部有死等大于32ms, 就有会导致任务执行时间不准确.

2.如果想在window验证, 由于list.h在visual studio会报错, 如果想验证需要安装gcc(在windows环境下用vscode配置gcc编译代码_windows vscode gcc-CSDN博客), 

贴出keil和gcc源码, 有积分的兄弟可以支持下.也可以不下, 我已经将所有代码贴出来了.

https://download.csdn.net/download/qq_38591801/88900090

4.代码仓库

        代码已放到gitee, 功能也会进一步完善, 如果有在使用中遇到bug, 可以在博客这边留言.

时间片框架: 基于时间片的裸机程序框架

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/185515
推荐阅读
相关标签
  

闽ICP备14008679号