赞
踩
有过电脑系统装机经历的人都知道 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)
即鸿蒙镜像的入口函数为 reset_vector
。而该函数的定义如下所示:
// ~/kernel/liteos_a/arch/arm/arm/src/startup/reset_vector_up.S
reset_vector:
//必要的初始化
......
bl main
.....
其中(下同):
从 main
函数开始就进入了我们熟悉的C语言环境了。其主要的工作,可从下面的代码一探究竟。
# ~/kernel/liteos_a/platform/main.c
main()
OsSetMainTask(); // 一
OsCurrTaskSet(OsGetMainTask()); //将当前task的信息写入寄存器
......
OsSystemInfo(); //打印系统信息
......
uwRet = OsMain(); // 二
......
OsStart(); // 三
while (1) {
__asm volatile("wfi");
}
从上述的代码段,我们可以看到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); } ...
从OsSetMainTask()
的代码,可以清晰地看到,在这个函数中,初始化了LOSCFG_KERNEL_CORE_NUM
个 LosTaskCB
,这个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();
&esmp;从OsMain()
函数的流程可以看到,启动过程中顺序地做了以下工作:
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; }
在系统初始化函数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); }
在OsSystemInitTaskCreate()
函数中创建了一个名为SystemInit
的 task
,这个 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();
在这个函数中有两个对后续流程的分析有关键作用的函数,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); }
OsStart()
函数首先获取最上面的那个task
,然后运行这个task
。函数OsStartToRun()
s是通过汇语言实现的,具体位置./kernel/liteos_a/arch/arm/arm/src/los_dispatch.S
。
至此,鸿蒙系统已经从引导程序中完全接过了控制权,启动了驱动程序的服务框架,并启动了用户空间的第一个进程init
,通过init
进程就可以“孵化”出用户需要的其他进程。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。