1. 程式人生 > >AliOS Things的啟動過程分析(二)

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的全部過程,其中有很多元件以及核心的初始化工作。為了能夠更好的實現自己的程式碼,對系統的整個流程有一個清晰的認識是很必要的工作。在本篇文章中,僅僅是簡單的分析了較為頂層的一個初始化流程,具體的細節會在後續的文章中繼續分析。