【鸿蒙系统HiSpark Wi-Fi IoT智能家居套件】多任务编程踩到的坑

末日大猩猩
发布于 2020-10-28 17:42
浏览
0收藏

因为拿不到套件跑的liteos_m内核的源码,所以一些涉及到深层次的东西目前还不清楚原理。

本文主要是列一下多任务编程过程中踩到的坑和遇到的问题,一是为了和大家分享一些经验,另一个也是希望有人能指出我的错误。闭门造车,画地为牢可不好。

直入主题吧~~~~

1. 任务栈溢出

任务内容如下

static void *Thread2(const char *arg)
{
    (void)arg;
    uint32_t kernelTimerCnt = 0;

    while(1) 
    {
        kernelTimerCnt = osKernelGetSysTimerCount();

        printf("2 = %u\n",kernelTimerCnt);
        usleep(3000);
    }
     
    return NULL;
}

创建任务时属性设置如下

#define TASK_STACK_SIZE 512

    attr.name = "thread2";
    attr.attr_bits = 0U;
    attr.cb_mem = NULL;
    attr.cb_size = 0U;
    attr.stack_mem = NULL;
    attr.stack_size = TASK_STACK_SIZE;
    attr.priority = osPriorityBelowNormal1;

    if (osThreadNew((osThreadFunc_t)Thread2, NULL, &attr) == NULL) {
        printf("[os_test] Falied to create Thread2!\n");
    }

在创建任务之前我特意查了一下任务栈空间的最小需求

printf("[os_test] g_taskMinStkSize = %u\n",g_taskMinStkSize);

输出结果:[os_test] g_taskMinStkSize = 384

而我的任务很简单对栈的需求不会太高,所以我设置栈空间大小为512,但是运行后报错了,如下

【鸿蒙系统HiSpark Wi-Fi IoT智能家居套件】多任务编程踩到的坑-鸿蒙开发者社区

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

 

2. 无法修改tick周期

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

获取tick周期(频率)和系统定时器周期(频率)

uint32_t timerFreq,tickFreq;

timerFreq = osKernelGetSysTimerFreq();
tickFreq = osKernelGetTickFreq();

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中,内容如下

#if (LOSCFG_LIB_CONFIGURABLE == YES)
extern UINT32                                               g_minusOneTickPerSecond;
#define LOSCFG_BASE_CORE_TICK_PER_SECOND                    (g_minusOneTickPerSecond + 1) /* tick per sencond plus 1 */
#else
#define LOSCFG_BASE_CORE_TICK_PER_SECOND                    (1000UL)
#endif

因为在编译的时候定义了LIB_CONFIGURABLE,所以配置tick频率的内容如下

extern UINT32                                               g_minusOneTickPerSecond;
#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周期。但我并不像去改变编译参数,所以并没有这样做。有时间这样试一试。修改编译参数:build\lite\config\kernel\liteos\cortex_m\BUILD.gn)

对于上面标红的内容,最近做了验证,验证结果:虽然可以修改宏定义OS_SYS_CLOCK和LOSCFG_BASE_CORE_TICK_PER_SECOND的值,然后调用osKernelGetSysTimerFreq和osKernelGetTickFreq发现确实和修改后的值相等。但是实际测试发现系统定时器频率和tick频率都没有变化,依然是160MHz和100Hz。

 

3. 任务之间的切换时间很奇怪

如下两个任务,Thread1的优先级为osPriorityBelowNormal,Thread2的优先级为osPriorityBelowNormal1。即Thread2优先级高于Thread1。

static void *Thread1(const char *arg)
{
    (void)arg;
    uint32_t kernelTimerCnt = 0;

    while(1) 
    {
        kernelTimerCnt = osKernelGetSysTimerCount();
 
        printf("1 = %u\n",kernelTimerCnt);
        usleep(2000);
    }

    return NULL;
}

static void *Thread2(const char *arg)
{
    (void)arg;
    uint32_t kernelTimerCnt = 0;

    while(1) 
    {
        kernelTimerCnt = osKernelGetSysTimerCount();

        printf("2 = %u\n",kernelTimerCnt);
        usleep(3000);
    }
     
    return NULL;
}

 

对应这样的两个任务我做了两个测试

测试一:

Thread1不休眠,一直运行,即屏蔽 usleep(2000);

查看输出,很明显的当Thread2需要运行时,会打断Thread1,当Thread2执行完休眠之后会跳回Thread1。

系统运行一段时间后就会复位。

以上测试结果基本上可以说明如下几个问题:

1. Thread2的优先级高于Thread1,当Thread2就绪时会抢占资源。

2. 因为Thread1一直没有释放资源,所以优先级低于Thread1的任务都无法运行,这会导致看门狗复位。

3. Thread2的输出周期并不是期望的3ms,而是10ms,是的,和tick的周期相同

 

测试二:

在测试一的基础上,不屏蔽usleep(2000);即代码如上。

输出情况如下

【鸿蒙系统HiSpark Wi-Fi IoT智能家居套件】多任务编程踩到的坑-鸿蒙开发者社区

可以看到从Thread1切换到Thread2,耗时约1600000 / 160000000 * 1000 =10ms.

而从Thread2切换到Thread1,耗时约3500 / 160000000 * 1000 =0.02ms.

然后我调换了两个任务的优先级,从输出结果看,两个任务之间的切换耗时也出现了调换。

也就是说,从低优先级任务切换到高优先级任务时,即使高优先级任务已经就绪,但是最快时间也要一个tick周期的时间,或许是因为系统任务的切换是在每次tick中断时进行的,而从高优先级任务切换到低优先级任务,却可以马上切换,这个有些不理解了。

这个现象真的是让人难以理解~~~~~

 

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

 

对于以上问题只是个人见解,有错误的地方还请指出;

有些疑问甚至没有给出答案,有明白的大佬希望能够不吝赐教。

 

 

 

 

 

 

已于2020-10-30 17:55:46修改
3
收藏
回复
举报
3条回复
按时间正序
/
按时间倒序
jiecho
jiecho

等源码放出,期待楼主更多内容产出

回复
2020-10-29 16:32:50
wx5d84d7546ffe0
wx5d84d7546ffe0

怎么配置tick的周期啊???

回复
2020-11-6 10:46:29
唐佐林
唐佐林

说说我对当前“实时性”的看法:

1. 目前的鸿蒙板载系统瞄准的物联网领域,多数任务只是数据采集,因此数据方便快捷的传输是重点,所以并没有在实时性上发力。

2. 目前的系统处于早期试用阶段,或许后续的版本会改进实时性。

 

这个板载系统源码未放出,具体内部如何设计的外界都不知道,恐怕想深入研究只有等开源了。

2
回复
2020-11-6 13:02:54
回复
    相关推荐