当前位置:   article > 正文

鸿蒙系列一:启动流程_devicemanagerstart

devicemanagerstart

引导程序

  有过电脑系统装机经历的人都知道 BIOS 的概念,为 Android 手机刷机过的小伙伴都听过Bootloader ,那这些概念和手机启动有什么关系呢?

  首先,无论是电脑还是手机,操作系统都不是开机就直接启动的,都是通过引导程序来帮助启动操作系统的,对于电脑来说 BIOS 起到了这个功能,对于手机则是 Bootloader 来担此重任。可能有些人会有疑问,电脑和手机的开机为什么不直接启动操作系统,而是需要通过引导程序来协助呢。这主要是由目前的硬件结构决定的。众所周知,不管是电脑还是手机操作系统都是运行在RAM 中的,而 RAM 在仅上电未进行一定初始化之前,是无法运行程序的。所以在RAM 运行操作系统之前,需要有一个程序来完成相应的初始化工作,这一艰巨的任务就交给了引导程序。

  那引导程序是如何工作的呢?在系统一上电的时候,CPU 会根据配置(比如配置从EMMC/SDCARD等启动方式)选择从某个固定位置来读取运行的第一行代码,这个地方存储的就是引导程序。引导程序运行后,进行必要的(最小)初始化(如:时钟、MMU等)后,将存储在 ROM 中的操作系统搬移到 RAM 中的某个位置(可能还会牵涉到解压的操作),接着引导程序会将控制权转移给RAM中的操作系统,启动操作系统。

操作系统的首指令

  从上面的分析可知,只要找到操作系统的首条指令(语句),就可以顺着这条线索,理清启动流程。鸿蒙镜像编译的链接文件(关键语句)如下:

// ~/kernel/liteos_a/tools/build/liteos_llvm.ld
ENTRY(reset_vector)
  • 1
  • 2

  即鸿蒙镜像的入口函数为 reset_vector。而该函数的定义如下所示:

// ~/kernel/liteos_a/arch/arm/arm/src/startup/reset_vector_up.S
reset_vector:
	//必要的初始化
	......
    bl     main
    .....
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

其中(下同):

  • “…”代表省略掉的干扰主体逻辑的代码
  • 首行//后的内容代表该代码段的来源文件

C 语言的天地

  从 main函数开始就进入了我们熟悉的C语言环境了。其主要的工作,可从下面的代码一探究竟。

# ~/kernel/liteos_a/platform/main.c
main()
    OsSetMainTask();					// 一
    OsCurrTaskSet(OsGetMainTask());		//将当前task的信息写入寄存器
	......
	OsSystemInfo();    					//打印系统信息
	......
	uwRet = OsMain();					// 二
	......
	OsStart();							// 三

	while (1) {
    	__asm volatile("wfi");
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

  从上述的代码段,我们可以看到main函数主要做了三方面的工作。接下来我们就从这个函数开始启动流程的分析。

第一步:OsSetMainTask()

// ~/kernel/liteos_a/kernel/base/core/los_task.c
LITE_OS_SEC_BSS STATIC LosTaskCB                g_mainTask[LOSCFG_KERNEL_CORE_NUM];

OsSetMainTask()
    ...
    for (i = 0; i < LOSCFG_KERNEL_CORE_NUM; i++) {
        g_mainTask[i].taskStatus = OS_TASK_STATUS_UNUSED;
        g_mainTask[i].taskID = LOSCFG_BASE_CORE_TSK_LIMIT;
        g_mainTask[i].priority = OS_TASK_PRIORITY_LOWEST;
#if (LOSCFG_KERNEL_SMP_LOCKDEP == YES)
        g_mainTask[i].lockDep.lockDepth = 0;
        g_mainTask[i].lockDep.waitLock = NULL;
#endif
        ret = memcpy_s(g_mainTask[i].taskName, OS_TCB_NAME_LEN, name, strlen(name));
        if (ret != EOK) {
            g_mainTask[i].taskName[0] = '\0';
        }
        LOS_ListInit(&g_mainTask[i].lockList);
    }
	...
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

  从OsSetMainTask()的代码,可以清晰地看到,在这个函数中,初始化了LOSCFG_KERNEL_CORE_NUMLosTaskCB,这个LosTaskCB数组用于记载当前系统中Task的控制信息(Control Block),包括优先级、状态、标志ID、锁的状态等等信息,可以根据命名略知一二。LOSCFG_KERNEL_CORE_NUM变量一般是根据主控芯片,来选择和是数值。至于修改可参考相关文档,不是启动流程的关键信息。

第二步:OsMain()

  OsMain()函数的主要代码如下所示:

// ~/kernel/liteos_a/kernel/common/los_config.c
OsMain()
    osRegister();
	......
    OsExcInit();
    OsTickInit();
	......
    ret = OsTaskInit();
	......
    ret = OsIpcInit();
	......
    OsSysMemInit();
	......
    SyscallHandleInit();
	.......
    ret = OsKernelInitProcess();		//1
	......
    ret = OsSystemInit();				//2 
	......
    ret = OsFutexInit();
	......
	ret = OomTaskInit();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

 &esmp;从OsMain()函数的流程可以看到,启动过程中顺序地做了以下工作:

  • 寄存器
  • 异常
  • 时钟
  • OsTaskInit
  • 进程间通信
  • 内存
  • OsKernelInitProcess
  • 系统初始化
  • 等等
      对于这些初始化工作,我们不都做分析,重点分析系统初始化函数OsSystemInit,因为后面好多重要的工作都是由这个函数引出的。
系统初始化OsSystemInit函数
// ~/kernel/liteos_a/kernel/common/los_config.c   
UINT32 OsSystemInit(VOID)
{
    UINT32 ret;
#ifdef LOSCFG_FS_VFS
    los_vfs_init();
#endif
#ifdef LOSCFG_COMPAT_LINUXKPI
    g_pstSystemWq = create_workqueue("system_wq");
#endif
    ret = OsSystemInitTaskCreate();			// (1)
    if (ret != LOS_OK) {
        return ret;
    }
#ifdef LOSCFG_MEM_RECORDINFO
    ret = OsMemShowTaskCreate();
    if (ret != LOS_OK) {
        PRINTK("create memshow_Task error %u\n", ret);
        return ret;
    }
    PRINTK("create memshow_Task ok\n");
#endif
#ifdef LOSCFG_KERNEL_TICKLESS
    LOS_TicklessEnable();
#endif

    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

  在系统初始化函数OsSystemInit中,除了OsSystemInitTaskCreate()函数外,其他函数都有宏定义的开关,也即这些宏定义包含下的函数是系统的一些“扩展”特性,非必须特性。所以我们的重点工作可以放在OsSystemInitTaskCreate()函数上。接下来,就进入OsSystemInitTaskCreate()函数的分析。

// ~/kernel/liteos_a/kernel/common/los_config.c 
STATIC UINT32 OsSystemInitTaskCreate(VOID)
{
    UINT32 taskID;
    TSK_INIT_PARAM_S sysTask;

    (VOID)memset_s(&sysTask, sizeof(TSK_INIT_PARAM_S), 0, sizeof(TSK_INIT_PARAM_S));
    sysTask.pfnTaskEntry = (TSK_ENTRY_FUNC)SystemInit;	// ①
    sysTask.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
    sysTask.pcName = "SystemInit";				
    sysTask.usTaskPrio = LOSCFG_BASE_CORE_TSK_DEFAULT_PRIO;
    sysTask.uwResved = LOS_TASK_STATUS_DETACHED;
#if (LOSCFG_KERNEL_SMP == YES)
    sysTask.usCpuAffiMask = CPUID_TO_AFFI_MASK(ArchCurrCpuid());
#endif
    return LOS_TaskCreate(&taskID, &sysTask);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

  在OsSystemInitTaskCreate()函数中创建了一个名为SystemInittask,这个 task的入口函数通过pfnTaskEntry属性来指定,指向函数 SystemInit()的入口地址。这个函数的具体实现在vendor 目录下,即对每个不同的产品(product),系统初始化需要的操作步骤是不同的。

  接下来,看看函数SystemInit()具体执行的操作,这个函数的执行的代码如下所示:

// ~/vendor/st/stm32mp157/board/board.c
SystemInit()							
    ProcFsInit();
	mem_dev_register();
	imx6ull_driver_init();
    imx6ull_mount_rootfs();
	DeviceManagerStart();				//HDF,加载驱动,使外射可以正常工作。
	uart_dev_init();
	......
    OsUserInitProcess();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

  在这个函数中有两个对后续流程的分析有关键作用的函数,DeviceManagerStart()OsUserInitProcess(),他们的作用分别为:

  • DeviceManagerStart():用于启动鸿蒙驱动框架(HDF)相关代码。
  • OsUserInitProcess():启动initt进程,其作用类似于linux中的init进程。

第三步:OsStart()

// ~/kernel/liteos_a/kernel/common/los_config.c
LITE_OS_SEC_TEXT_INIT VOID OsStart(VOID)
{
    LosProcessCB *runProcess = NULL;
    LosTaskCB *taskCB = NULL;
    UINT32 cpuid = ArchCurrCpuid();

    OsTickStart();

    LOS_SpinLock(&g_taskSpin);
    taskCB = OsGetTopTask();		// 获取最上面的Task

    runProcess = OS_PCB_FROM_PID(taskCB->processID);
    runProcess->processStatus |= OS_PROCESS_STATUS_RUNNING;
#if (LOSCFG_KERNEL_SMP == YES)
    /*
     * attention: current cpu needs to be set, in case first task deletion
     * may fail because this flag mismatch with the real current cpu.
     */
    taskCB->currCpu = cpuid;
    runProcess->processStatus = OS_PROCESS_RUNTASK_COUNT_ADD(runProcess->processStatus);
#endif

    OS_SCHEDULER_SET(cpuid);

    PRINTK("cpu %d entering scheduler\n", cpuid);
    OsStartToRun(taskCB);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

  OsStart()函数首先获取最上面的那个task,然后运行这个task。函数OsStartToRun()s是通过汇语言实现的,具体位置./kernel/liteos_a/arch/arm/arm/src/los_dispatch.S

  至此,鸿蒙系统已经从引导程序中完全接过了控制权,启动了驱动程序的服务框架,并启动了用户空间的第一个进程init,通过init进程就可以“孵化”出用户需要的其他进程。

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

闽ICP备14008679号