发 帖  
原厂入驻New
[经验]

【HarmonyOS HiSpark Wi-Fi IoT套件】多任务编程踩到的坑

2020-10-30 12:03:09  122 Hispark开发套件
分享
1
原文链接:https://harmonyos.51cto.com/posts/1461

因为拿不到套件跑的liteos_m内核的源码,所以一些涉及到深层次的东西目前还不清楚原理。
本文主要是列一下多任务编程过程中踩到的坑和遇到的问题,一是为了和大家分享一些经验,另一个也是希望有人能指出我的错误。闭门造车,画地为牢可不好。
直入主题吧~~~~
1. 任务栈溢出
任务内容如下
  1. static void *Thread2(const char *arg)
  2. {
  3.     (void)arg;
  4.     uint32_t kernelTimerCnt = 0;

  5.     while(1)
  6.     {
  7.         kernelTimerCnt = osKernelGetSysTimerCount();

  8.         printf("2 = %u\n",kernelTimerCnt);
  9.         usleep(3000);
  10.     }
  11.      
  12.     return NULL;
  13. }
复制代码

创建任务时属性设置如下
  1. #define TASK_STACK_SIZE 512

  2.     attr.name = "thread2";
  3.     attr.attr_bits = 0U;
  4.     attr.cb_mem = NULL;
  5.     attr.cb_size = 0U;
  6.     attr.stack_mem = NULL;
  7.     attr.stack_size = TASK_STACK_SIZE;
  8.     attr.priority = osPriorityBelowNormal1;

  9.     IF (osThreadNew((osThreadFunc_t)Thread2, NULL, &attr) == NULL) {
  10.         printf("[os_test] Falied to create Thread2!\n");
  11.     }
复制代码

在创建任务之前我特意查了一下任务栈空间的最小需求
  1. printf("[os_test] g_taskMinStkSize = %u\n",g_taskMinStkSize);
复制代码

输出结果:[os_test] g_taskMinStkSize = 384
而我的任务很简单对栈的需求不会太高,所以我设置栈空间大小为512,但是运行后报错了,如下

即栈溢出了。当我把栈空间增大后,问题解决。虽然问题很简单的解决了,但是系统对任务栈空间的需求让我感觉有些太大了。因为不清楚内部实现,所以也不清楚栈空间中都存储了哪些东西。

2. 无法修改tick周期
在做机器人系统底层设计的时候往往需要较快的响应速度,所以要求任务之间的切换要尽量快,不清楚套件中liteos_m的任务切换方式是什么,不过一般来说应该是和tick周期有关,所以我这里想要看一下当前的tick周期,然后尝试修改。
获取tick周期(频率)和系统定时器周期(频率)
  1. uint32_t timeRFreq,tickFreq;

  2. timerFreq = osKernelGetSysTimerFreq();
  3. tickFreq = osKernelGetTickFreq();

  4. printf("[os_test] timerFreq = %u, tickFreq = %u\n",timerFreq,tickFreq);
复制代码

输出:timerFreq = 160000000, tickFreq = 100
如上,系统定时器频率是160MHz,tick频率是100Hz
然后我开始查找配置接口,位于vendor\hisi\hi3861\hi3861\platform\os\Huawei_LiteOS\targets\hi3861v100\include\target_config.h中,内容如下
  1. #if (LOSCFG_LIB_CONFIGURABLE == YES)
  2. extern UINT32                                               g_minuSONETickPerSecond;
  3. #define LOSCFG_BASE_CORE_TICK_PER_SECOND                    (g_minusOneTickPerSecond + 1) /* tick per sencond plus 1 */
  4. #else
  5. #define LOSCFG_BASE_CORE_TICK_PER_SECOND                    (1000UL)
  6. #endif
复制代码

因为在编译的时候定义了LIB_CONFIGURABLE,所以配置tick频率的内容如下
  1. extern UINT32                                               g_minusOneTickPerSecond;
  2. #define LOSCFG_BASE_CORE_TICK_PER_SECOND                    (g_minusOneTickPerSecond + 1) /* tick per sencond plus 1 */
复制代码

然后开始查找g_minusOneTickPerSecond,通过查找编译的map和asm文件发现这个变量不在可修改的源文件中,应该是在未开放的代码部分。而且并没有提供修改该变量的接口。不过没关系,既然可以使用extern声明,那么我们就可以使用,于是在创建任务之前我修改了g_minusOneTickPerSecond=999,即期望设置tick频率为1000.可想而知,起始tick的频率并没有改变,依然是100.很正常,因为内核应该会使用这个信息来配置定时器中断的周期,所以这种配置是需要在初始化内核之前完成的。
那就继续查找,看一下内核初始化是在哪里进行,我只要在此之前修改这个变量就好了。
通过asm发现了系统启动过程中函数调用的关系:start_flash_data_loop  --》 main --》 AppInit --》app_main。而前三个函数都没有对外开放,我们看不到内容,更无法修改。那就只能看一看app_main了,很遗憾,这里已经进行到应用级别了,内核初始化早已经完成。
从以上内容看,我应该是无法修改tick的周期了。(理论上编译的时候不要定义LIB_CONFIGURABLE,然后应该就可以通过修改配置文件vendor\hisi\hi3861\hi3861\platform\os\Huawei_LiteOS\targets\hi3861v100\include\target_config.h中的宏定义来修改tick周期。但我并不像去改变编译参数,所以并没有这样做。有时间这样试一试。修改编译参数:vendor\hisi\hi3861\hi3861\build\make_scripts\config.mk)

3. 任务之间的切换时间很奇怪
如下两个任务,Thread1的优先级为osPriorityBelowNormal,Thread2的优先级为osPriorityBelowNormal1。即Thread2优先级高于Thread1。
  1. <pre class="language-c"><code>static void *Thread1(const char *arg)
  2. {
  3.     (void)arg;
  4.     uint32_t kernelTimerCnt = 0;

  5.     while(1)
  6.     {
  7.         kernelTimerCnt = osKernelGetSysTimerCount();

  8.         printf("1 = %u\n",kernelTimerCnt);
  9.         usleep(2000);
  10.     }

  11.     return NULL;
  12. }

  13. static void *Thread2(const char *arg)
  14. {
  15.     (void)arg;
  16.     uint32_t kernelTimerCnt = 0;

  17.     while(1)
  18.     {
  19.         kernelTimerCnt = osKernelGetSysTimerCount();

  20.         printf("2 = %u\n",kernelTimerCnt);
  21.         usleep(3000);
  22.     }
  23.      
  24.     return NULL;
  25. }</code></pre>
复制代码

对应这样的两个任务我做了两个测试
测试一:
Thread1不休眠,一直运行,即屏蔽 usleep(2000);
查看输出,很明显的当Thread2需要运行时,会打断Thread1,当Thread2执行完休眠之后会跳回Thread1。
系统运行一段时间后就会复位。
以上测试结果基本上可以说明如下几个问题:
1. Thread2的优先级高于Thread1,当Thread2就绪时会抢占资源。
2. 因为Thread1一直没有释放资源,所以优先级低于Thread1的任务都无法运行,这会导致看门狗复位。
3. Thread2的输出周期并不是期望的3ms,而是10ms,是的,和tick的周期相同

测试二:
在测试一的基础上,不屏蔽usleep(2000);即代码如上。
输出情况如下

可以看到从Thread1切换到Thread2,耗时约1600000 / 160000000 * 1000 =10ms.
而从Thread2切换到Thread1,耗时约3500 / 160000000 * 1000 =0.02ms.
然后我调换了两个任务的优先级,从输出结果看,两个任务之间的切换耗时也出现了调换。
也就是说,从低优先级任务切换到高优先级任务时,即使高优先级任务已经就绪,但是最快时间也要一个tick周期的时间,或许是因为系统任务的切换是在每次tick中断时进行的,而从高优先级任务切换到低优先级任务,却可以马上切换,这个有些不理解了。
这个现象真的是让人难以理解~~~~~

总的来说,多任务下无法设置tick周期,任务切换时间如此奇怪,这种问题在对实时性要求比较高的机器人底层系统中是无法接受的。

对于以上问题只是个人见解,有错误的地方还请指出;
有些疑问甚至没有给出答案,有明白的大佬希望能够不吝赐教。


钟李聪 2020-11-6 10:45:40
不能设置tick是最大的问题!好烦!
回复

举报

只有小组成员才能发言,加入小组>>

1087个成员聚集在这个小组

加入小组

创建小组步骤

关闭

站长推荐 上一条 /9 下一条

快速回复 返回顶部 返回列表