AliOS Things的啓動過程分析(二)

AliOS Things的啓動過程分析(二)

在AliOS Things的啓動過程分析(一)中分析了developerkit從系統上電到調用main函數所經歷的一些步驟,接下來詳細分析一下main函數的一些工作,主要是內核的相關初始化工作。main函數所處的位置位於 platform\mcu\stm32l4xx_cube\aos\aos.c文件中。下面是main函數的代碼。

int main(void)
{
    sys_start();
    return 0;
}

static void sys_start(void)
{
    aos_heap_set();   //設置堆的起始地址和長度
    
    aos_init();		//即內核初始化,在其中完成了必要的初始化(內存初始化、內核對象初始化)、新建立了幾個默認的任務(定時器任務、空閒任務、工作隊列等)
//建立sys_init任務,進入應用層代碼
    krhino_task_create(&demo_task_obj, "aos-init", 0,AOS_DEFAULT_APP_PRI, 
        0, demo_task_buf, AOS_START_STACK, (task_entry_t)sys_init, 1);
    
    aos_start();   //即內核啓動。代表開始任務調度
}

void aos_heap_set(void)
{
    g_mm_region[0].start = (uint8_t*)&__bss_end__;   //在ld鏈接文件中指定的地址
    g_mm_region[0].len   = 
        ((uint8_t*)&_estack - (uint8_t*)&__bss_end__) - RHINO_CONFIG_SYSTEM_STACK_SIZE;
                                     //RAM區的尾地址-bss段的結束地址-偏移地址
}

在main函數之中完成的工作是首先要使用aos_heap_set進行堆區域的設置,之後使用aos_init函數(其實是對於krhino_init函數的封裝)進行內核的初始化,在內核初始化之後,建立一個sys_init的系統任務,在該系統任務中會調用應用業務層代碼application_start()函數。前面已經介紹了aos_heap_set函數的主要目的,接下來分析aos_init(即krhino_init的源碼)

kstat_t krhino_init(void)
{
    g_sys_stat = RHINO_STOPPED;   //設置系統狀態爲STOPPED

#if (RHINO_CONFIG_USER_HOOK > 0)    //鉤子函數,默認是開啓的
    krhino_init_hook();            //鉤子函數初始化
#endif

#if (RHINO_CONFIG_CPU_NUM > 1)      //如果是多核的處理器,因爲developer僅僅有一個CPU,所以處理不會執行
    krhino_spin_lock_init(&g_sys_lock);
#endif

//運行隊列初始化,系統中如果有多個任務並行處理的話,建立過多的任務對於系統的壓力很大,使用工作隊列同樣可以
//實現任務的相關操作,減輕系統壓力
    runqueue_init(&g_ready_queue);  

    tick_list_init();//初始化g_tick_head雙向鏈表

/*內核對象初始化,內核對象數據結構爲:kobj_list_t 內部包含:互斥量   信號量  消息隊列  內存池 事件等*/
#if (RHINO_CONFIG_SYSTEM_STATS > 0)
    kobj_list_init();
#endif

//內存初始化,根據前面aos_heap_init函數的g_mm_region參數對內存進行初始化
#if (RHINO_CONFIG_MM_TLF > 0)
    k_mm_init();
#endif

//動態內存管理初始化,
#if (RHINO_CONFIG_KOBJ_DYN_ALLOC > 0)
    klist_init(&g_res_list);
    krhino_sem_create(&g_res_sem, "res_sem", 0);
    dyn_mem_proc_task_start();
#endif


#if (RHINO_CONFIG_CPU_NUM > 1)
    for (uint8_t i = 0; i < RHINO_CONFIG_CPU_NUM; i++) {
        krhino_task_cpu_create(&g_idle_task[i], "idle_task", NULL, RHINO_IDLE_PRI, 0,
                               &g_idle_task_stack[i][0], RHINO_CONFIG_IDLE_TASK_STACK_SIZE,
                               idle_task, i, 1u);
    }
#else  //單核處理器創建空閒任務,系統空閒時運行該任務
    krhino_task_create(&g_idle_task[0], "idle_task", NULL, RHINO_IDLE_PRI, 0,
                       &g_idle_task_stack[0][0], RHINO_CONFIG_IDLE_TASK_STACK_SIZE,
                       idle_task, 1u);
#endif

//工作隊列初始化
#if (RHINO_CONFIG_WORKQUEUE > 0)
    workqueue_init();
#endif

//定時器初始化,建立一個定時器任務,用於管理系統的定時器列。
#if (RHINO_CONFIG_TIMER > 0)
    ktimer_init();
#endif

    rhino_stack_check_init();

    return RHINO_SUCCESS;
}

以上爲內核的初始化部分的相關介紹,主要是新建立了幾個任務,初始化了一些必要的內核對象等。內核初始化的細節部分沒有具體去介紹,因爲內核部分涉及到的數據結構較多,在此處進行介紹,篇幅較長,以後建立一個內核的章節來介紹其內部實現細節。

在完成了內核的初始化工作之後,會調用任務建立接口新建立一個sys_init的任務,該任務會進行板級的相關初始化,並調用業務層代碼入口函數application_start。

新建立了任務僅僅是在任務隊列中加入了該任務,任務並沒有開始調度執行,aos_start(即krhino_start)爲內核啓動進入調度的函數。此時sys_init任務才能夠運行起來。

aos_start內核調度函數如下所示:

kstat_t krhino_start(void)
{
    ktask_t *preferred_task;

    if (g_sys_stat == RHINO_STOPPED) {  //當前系統爲STOPPED狀態
#if (RHINO_CONFIG_CPU_NUM > 1)     //多核處理器的調度方法
        for (uint8_t i = 0; i < RHINO_CONFIG_CPU_NUM; i++) {
            preferred_task            = preferred_cpu_ready_task_get(&g_ready_queue, i);
            preferred_task->cpu_num   = i;
            preferred_task->cur_exc   = 1;
            g_preferred_ready_task[i] = preferred_task;
            g_active_task[i]          = g_preferred_ready_task[i];
            g_active_task[i]->cur_exc = 1;
        }
#else          //單核處理器
       //從RDY隊列中取出最高優先級的任務
        preferred_task = preferred_cpu_ready_task_get(&g_ready_queue, 0);
        g_preferred_ready_task[0] = preferred_task;//保存位於RDY隊列中的當前最高優先級的任務
        g_active_task[0] = preferred_task;  //當前運行的任務
#endif

#if (RHINO_CONFIG_USER_HOOK > 0)
        krhino_start_hook();   //啓動鉤子函數,主要用於記錄當前任務的時間信息
#endif

        g_sys_stat = RHINO_RUNNING;  //修改系統狀態爲RUNNING
        cpu_first_task_start();  //任務啓動,設置任務控制塊

        /* should not be here */
        return RHINO_SYS_FATAL_ERR;
    }

    return RHINO_RUNNING;
}

aos_start函數運行起來之後,相當於此時內核已經完成了調度。當前系統中除了默認建立的空閒任務和定時器任務之外,還有一個剛剛建立的sys_init的任務。接下里分析一下sys_init函數所完成的主要功能:

sys_init函數代碼如下所示,這是系統開始運行的第一個和硬件版以及應用程序有關的任務代碼,前面的初始化部分僅僅是內核相關的初始化代碼。在該函數中完成的功能是:

  1. SoC處理器與硬件開發板相關初始化
  2. hal
  3. 開發板的flash分區以及cli組件初始化
  4. 嚮應用程序傳遞參數初始化
  5. alios things相關的組件初始化
static void sys_init(void)
{
/*在該函數中主要完成flash 預存取 、數據以及指令緩存使能或者關閉,
//設置NVIC 組別,設置時鐘時基標準、設置。以及進行HAL_Mspinit硬件初始化*/
    stm32_soc_init();
#ifdef BOOTLOADER
    main();
#else
//進行OTA對象的初始化以及wifi的初始化
    hw_start_hal();
//對flash區域進行邏輯分析,設置各個分區的起始和終止地址
//新建一個fota信號量
    board_init();
//傳參初始化,設置傳參個數,數據地址,以及是否使能cli組件
    var_init();
#ifdef AOS_CPLUSPLUS
    cpp_init();
#endif
//函數完成的功能是各個組件的初始化以及進入應用程序
    aos_kernel_init(&kinit);
#endif
}

接下來主要分析aos_kernel_init(&kinit);函數,以註釋的形式進行分析

int aos_kernel_init(kinit_t *kinit)
{
  //VFS虛擬文件系統初始化,詳情可以參考https://www.yuque.com/beiqiang/linux_related/itm2vp  的介紹
#ifdef AOS_VFS
    vfs_init();  //建立VFS互斥鎖,初始化inode數組
    vfs_device_init();  //註冊/dev/event節點以及操作方法
#endif
    
#ifdef CONFIG_AOS_CLI
    cli_service_init(kinit);  //CLI組件的初始化,並將參數傳遞進來
#else
    extern void log_no_cli_init(void);
    log_no_cli_init();
#endif
    
#ifdef AOS_KV
    aos_kv_init();   //KV組件存儲初始化
#endif

#ifdef WITH_SAL
    sal_device_init();  //SAL網絡框架初始化
#endif

#ifdef AOS_LOOP
    aos_loop_init();   //在主任務中系統默認創建了一個YLOOP實例
#endif

#ifdef OSAL_RHINO
    trace_start();
#endif

#ifdef AOS_UOTA 
    ota_service_init(); //OTA固件升級初始化
#endif

#ifdef AOS_SENSOR
    sensor_init(); //傳感器模塊初始化
#endif

#ifdef AOS_GPS
    gps_init();
#endif


// auto_component generated by the compiler system, now gcc support
#if defined (__GNUC__) && !defined (__CC_ARM)
    //aos_components_init();
#endif

#ifdef AOS_BINS
    app_pre_init();

#ifdef AOS_FRAMEWORK_COMMON
        aos_framework_init(); //MESH網絡的初始化工作,uData傳感器框架的初始化工作
#endif

    if (app_info->app_entry) {
#if (RHINO_CONFIG_USER_SPACE > 0)
        app_info->app_entry(0, NULL);
#else
        app_info->app_entry((void *)kmbins_tbl, 0, NULL);
#endif
    }
#else

#if (RHINO_CONFIG_CPU_USAGE_PERIOD > 0)  //CPU 使用記錄
    krhino_task_cpu_usage_init();
#endif

    application_start(kinit->argc, kinit->argv);  //調用業務層代碼入口函數
#endif

    return 0;
}

總結

以上就是從系統開始進入main函數到調用業務層函數入口application_start的全部過程,其中有很多組件以及內核的初始化工作。爲了能夠更好的實現自己的代碼,對系統的整個流程有一個清晰的認識是很必要的工作。在本篇文章中,僅僅是簡單的分析了較爲頂層的一個初始化流程,具體的細節會在後續的文章中繼續分析。

轉載

AliOS Things的啓動過程分析(二)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章