lite os 启动流程分析
拿到开发板一段时间了,论坛里各个开箱报告都写的很好,感觉自己拍马难及,所以本篇文章想从另外一个角度分享下自己的使用心得,粗略的分析下liteos-a的启动流程,抛装引玉。希望大神勿喷。哈哈哈哈。
一、启动的汇编文件 一般来说可以通过连接脚本来找到内核启动的入口,在liteos_a的目录下执行make命令,在编译输出的最后链接的时候,其中-T选项是指定的链接脚本,为kernelliteos_atoolsbuildliteos_llvm.ld
在这个文件中第一行为:
ENTRY(reset_vector) 也就是说入口地址为reset_vector 段,不难想象这个就是复位向量表的地址,文件为:kernelliteos_aarcharmarmsrcstartupreset_vector_up.c文件,在这个文件的目录下还有一个 reset_vector_mp.c文件,他是多核的启动文件。
还有一个比较笨的方法就是通过文件目录去找,一般内核的启动文件肯定是一个与架构相关的汇编文件,通过目录可以也很容易的找到。刚开始我就是通过方法来找的。
汇编文件的入口为:
reset_vector: 主要做以下事情
1 首先关闭i-cache d0cache 关掉MMU
2 如果需要重定位则进行内核的重定位,
2 建立内核和外设的内存映射,或者说是页表,这里采用一级页表大小为1M,
3 进行栈的初始化,
4 初始化cpsr寄存器,
5 清除BSS段,
6 跳入main函数
后续如有必要会详细分析下整个内核的内存管理。
二、系统的初始化的分析LITE_OS_SEC_TEXT_INIT INT32 main(VOID)
{
UINT32 uwRet = LOS_OK;
//设置主任务 ,即给每个核设置一个main task 暂时不去分析,等到任务切换的时候再看
OsSetMainTask();
OsCurrTaskSet(OsGetMainTask());
/* set smp system counter freq */
#if (LOSCFG_KERNEL_SMP == YES)
#ifndef LOSCFG_TEE_ENABLE
HalClockFreqWrite(OS_SYS_CLOCK);
#endif
#endif
/* system and chip info */
OsSystemInfo(); //打印kernel的一些信息,此时还没有初始化串口,所以这些信息应该只是放在buf中,
PRINT_RELEASE("nmain core booting up...n");
PRINT_RELEASE("This is my hello world in kernel!n"); //打印自己定义的调试信息
uwRet = OsMain(); //这个函数用来进行kernel的初始化工作 下面对这个函数进行分析
if (uwRet != LOS_OK) {
return LOS_NOK;
}
#if (LOSCFG_KERNEL_SMP == YES)
PRINT_RELEASE("releasing %u secondary coresn", LOSCFG_KERNEL_SMP_CORE_NUM - 1);
release_secondary_cores();
#endif
CPU_MAP_SET(0, OsHwIDGet());
OsStart(); //启动kernel
while (1) {
__asm volatile("wfi");
}
}
OsMain()函数的分析这个函数在 code-1.0kernelliteos_akernelcommonLos_config.c 在这个位置,这个里面主要是对内核的各个部分进行初始化,我们只关注与启动流程相关的 ,
LITE_OS_SEC_TEXT_INIT INT32 OsMain(VOID)
{
UINT32 ret;
osRegister(); // 只是简单的将一些宏赋给变量 比如#define LOSCFG_BASE_CORE_TICK_PER_SECOND 100 用来定义系统的调度周期 也就是10ms 所以内核的sys_tick默认是10ms
#ifdef LOSCFG_SHELL_DMESG
ret = OsDmesgInit();
if (ret != LOS_OK) {
return ret;
}
#endif
#ifdef LOSCFG_SHELL_LK
OsLkLoggerInit(NULL);
#endif
#ifdef LOSCFG_EXC_INTERACTION
#ifdef LOSCFG_ARCH_CORTEX_M7
/* 4096: 4K space for Stack */
ret = OsMemExcInteractionInit((UINT32)&__bss_end + 4096);
#else
ret = OsMemExcInteractionInit((UINTPTR)&__bss_end);
#endif
if (ret != LOS_OK) {
return ret;
}
#endif
#if (LOSCFG_PLATFORM_HWI == YES)
OsHwiInit();
#endif
OsExcInit();
ret = OsTickInit(g_sysClock, LOSCFG_BASE_CORE_TICK_PER_SECOND); //初始化硬件时钟,
if (ret != LOS_OK) {
return ret;
}
#ifdef LOSCFG_PLATFORM_UART_WITHOUT_VFS
#ifdef LOSCFG_DRIVERS
uart_init(); //uart 初始化
#endif
#ifdef LOSCFG_SHELL
#endif //LOSCFG_SHELL
#endif //LOSCFG_PLATFORM_UART_WITHOUT_VFS
ret = OsTaskInit(); //task 初始化
if (ret != LOS_OK) {
PRINT_ERR("OsTaskInit errorn");
return ret;
}
#if ((LOSCFG_BASE_IPC_QUEUE == YES) || (LOSCFG_BASE_IPC_MUX == YES) || (LOSCFG_BASE_IPC_SEM == YES))
ret = OsIpcInit(); //进程通信初始化
if (ret != LOS_OK) {
return ret;
}
#endif
ret = OsSysMemInit(); //系统内存初始化
if (ret != LOS_OK) {
PRINT_ERR("OsSysMemInit errorn");
return ret;
}
SyscallHandleInit(); //系统调用初始化
/*
* CPUP should be inited before first task creation which depends on the semaphore
* when LOSCFG_KERNEL_SMP_TASK_SYNC is enabled. So don't change this init sequence
* if not neccessary. The sequence should be like this:
* 1. OsIpcInit
* 2. OsCpupInit
* 3. other inits have task creation
*/
#ifdef LOSCFG_KERNEL_CPUP
ret = OsCpupInit();
if (ret != LOS_OK) {
PRINT_ERR("OsCpupInit errorn");
return ret;
}
#endif
ret = OsKernelInitProcess(); //内核进程的初始化
if (ret != LOS_OK) {
return ret;
}
#if (LOSCFG_BASE_CORE_SWTMR == YES)
ret = OsSwtmrInit();
if (ret != LOS_OK) {
return ret;
}
#endif
#ifdef LOSCFG_KERNEL_CPUP
OsCpupGuardCreator();
#endif
#if (LOSCFG_KERNEL_SMP == YES)
(VOID)OsMpInit();
#endif
#if defined(LOSCFG_HW_RANDOM_ENABLE) || defined (LOSCFG_DRIVERS_RANDOM)
random_alg_context.ra_init_alg(NULL);
run_harvester_iterate(NULL);
#endif
#ifdef LOSCFG_COMPAT_BSD
ret = OsBsdInit();
if (ret != LOS_OK) {
PRINT_ERR("init bsd failed!n");
return ret;
}
#endif
#ifdef LOSCFG_KERNEL_PIPE
OsDriverPipeInit();
#endif
ret = OsSystemInit(); // 在这个里面会创建一些task 用来做一些特定的初始化 下面会分析这个函数
if (ret != LOS_OK) {
return ret;
}
#if LOSCFG_DRIVERS_HIEVENT
OsDriverHiEventInit();
#endif
#if (LOSCFG_KERNEL_TRACE == YES)
LOS_TraceInit();
#endif
#if (LOSCFG_KERNEL_LITEIPC == YES)
ret = LiteIpcInit();
if (ret != LOS_OK) {
return ret;
}
#endif
#if (LOSCFG_BASE_CORE_HILOG == YES)
ret = HiLogDriverInit();
if (ret != LOS_OK) {
return ret;
}
#endif
#ifdef LOSCFG_KERNEL_VDSO
ret = OsInitVdso();
if (ret != LOS_OK) {
return ret;
}
#endif
ret = OsFutexInit();
if (ret != LOS_OK) {
PRINT_ERR("Create futex failed : %d!n", ret);
return ret;
}
ret = OomTaskInit();
if (ret != LOS_OK) {
return ret;
}
return LOS_OK;
}
OsSystemInit(); 函数分析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(); //创建一个系统初始化的任务
if (ret != LOS_OK) {
return ret;
}
ret = OsMyTaskCreate(); //创建一个自己测试的任务
if (ret != LOS_OK) {
return ret;
}
PRINTK("OsMyTaskCreate!!!n"); //打印测试的信息
#ifdef LOSCFG_MEM_RECORDINFO
ret = OsMemShowTaskCreate();
if (ret != LOS_OK) {
PRINTK("create memshow_Task error %un", ret);
return ret;
}
PRINTK("create memshow_Task okn");
#endif
#ifdef LOSCFG_KERNEL_TICKLESS
LOS_TicklessEnable();
#endif
return 0;
}
自己测试任务的执行函数如下,每3秒打印一个信息
static void MyKerneltask(VOID)
{
while(1)
{
PRINTK("MyKerneltaskn");
sleep(3);
}
}
系统初始化任务的执行函数如下,初始化各种应用层相关的驱动 文件系统 等等
最终会挂载根文件系统,启动init进程
void SystemInit(void)
{
#ifdef LOSCFG_PLATFORM_HISI_AMP
extern int _ipcm_vdd_init(void);
dprintf("ipcm init ...n");
_ipcm_vdd_init();
#ifndef LOSCFG_ARCH_CORTEX_M7
extern int sharefs_client_init(const char *path);
dprintf("sharefs init ...n");
sharefs_client_init("/sharefs");
#endif
extern int virt_tty_dev_init(void);
dprintf("virt tty init ...n");
virt_tty_dev_init();
#endif
#ifdef LOSCFG_DRIVERS_RANDOM
dprintf("random dev init ...n");
extern int ran_dev_register(void);
ran_dev_register();
#endif
#ifdef LOSCFG_FS_PROC
dprintf("proc fs init ...n");
extern void ProcFsInit(void);
ProcFsInit();
#endif
#ifdef LOSCFG_DRIVERS_MMC
dprintf("MMC dev init ...");
extern int SD_MMC_Host_init(void);
SD_MMC_Host_init();
#endif
#ifdef LOSCFG_DRIVERS_MEM
dprintf("mem dev init ...n");
extern int mem_dev_register(void);
mem_dev_register();
#endif
#ifndef LOSCFG_DRIVERS_HDF
#ifdef LOSCFG_DRIVERS_GPIO
dprintf("gpio init ...n");
extern int gpio_dev_init(void);
gpio_dev_init();
#endif
#endif
#ifdef LOSCFG_DRIVERS_HIEDMAC
extern int hiedmac_init(void);
hiedmac_init();
#endif
#ifdef LOSCFG_DRIVERS_HIDMAC
dprintf("dmac init ...n");
#endif
#ifdef LOSCFG_DRIVERS_MTD_NAND
dprintf("nand init ...n");
extern int nand_init(void);
if (!nand_init()) {
extern int add_mtd_partition(char *type, UINT32 start_addr, UINT32 length, UINT32 partition_num);
extern int mount(const char *source, const char *target,
const char *filesystemtype, unsigned long mountflags,
const void *data);
}
#endif
#ifdef LOSCFG_DRIVERS_MTD_SPI_NOR
dprintf("spi nor flash init ...n");
extern int spinor_init(void);
if (!spinor_init()) {
dprintf("spinor_init ...n");
}
#endif
#ifdef LOSCFG_DRIVERS_NETDEV
dprintf("net init ...n");
net_init();
dprintf("n************************************************************n");
#endif
#ifdef LOSCFG_DRIVERS_USB
dprintf("u*** init ...n");
u***_init(DEVICE, DEV_ETHERNET);
#endif
dprintf("Date:%s.n", __DATE__);
dprintf("Time:%s.n", __TIME__);
#ifndef LOSCFG_DRIVERS_HDF_PLATFORM_HISI_SDK
dprintf("calling SDK_init form HISI_SDK");
SDK_init();
dprintf("calling SDK_init form HISI_SDK done!");
#endif
#ifdef LOSCFG_PLATFORM_ROOTFS
dprintf("OsMountRootfs start ...n");
OsMountRootfs(); //挂载根文件系统
dprintf("OsMountRootfs end ...n");
#endif
#ifdef LOSCFG_DRIVERS_HDF
dprintf("DeviceManagerStart start ...n");
if (DeviceManagerStart()) {
PRINT_ERR("No drivers need load by hdf manager!");
}
dprintf("DeviceManagerStart end ...n");
#endif
#ifdef LOSCFG_DRIVERS_HDF_PLATFORM_UART
dprintf("virtual_serial_init start ...n");
if (virtual_serial_init(TTY_DEVICE) != 0) {
PRINT_ERR("virtual_serial_init failed");
}
dprintf("virtual_serial_init end ...n");
if (system_console_init(SERIAL) != 0) {
PRINT_ERR("system_console_init failedn");
}
dprintf("system_console_init end ...n");
#endif
#ifdef LOSCFG_SHELL
dprintf("CatLogShell start ...n");
CatLogShell();
dprintf("CatLogShell start ...n");
#endif
dprintf("OsUserInitProcess start ...n");
if (OsUserInitProcess()) { //启动用户进程
PRINT_ERR("Create user init process faialed!n");
return;
}
dprintf("OsUserInitProcess end ...n");
return;
}
三、用户进程的启动LITE_OS_SEC_TEXT_INIT UINT32 OsUserInitProcess(VOID)
{
//在这个函数中会创建一个用户进程
LosProcessCB *processCB = OS_PCB_FROM_PID(g_userInitProcess);
et = OsProcessCreateInit(processCB, OS_USER_MODE, "Init", OS_PROCESS_USERINIT_PRIORITY);
if (ret != LOS_OK) {
return ret;
}
// 用户进程的起始地址为:
CHAR *userInitTextStart = (CHAR *)&__user_init_entry;
}
上面的起始地址为用户进程的首地址
在code-1.0kernelliteos_akernelusersrclos_user_init.c 中
LITE_USER_SEC_ENTRY VOID OsUserInit(VOID *args)
{
printf("OsUserInit startn"); //打印调试信息
#ifdef LOSCFG_KERNEL_DYNLOAD
sys_call3(__NR_execve, (UINTPTR)g_initPath, 0, 0); //执行系统调用来执行elf文件 g_initPath = "/bin/init"
#endif
while (1) {
}
}
/bin/init 即:
code-1.0kernelliteos_aappsinitsrcinit.c 此时在用户模式 ,
int main(int argc, char * const *argv)
{
int ret;
const char *shellPath = "/bin/shell"; // fork一个shell进程
ret = fork();
if (ret < 0) {
printf("Failed to fork for shelln");
} else if (ret == 0) {
(void)execve(shellPath, NULL, NULL);
exit(0);
}
printf("exec shell n"); //打印调试信息
while (1) {
ret = waitpid(-1, 0, WNOHANG);
if (ret == 0) {
sleep(1);
}
};
}
启动shell
code-1.0kernelliteos_aappsshellsrcmain.c
int main()
{
....... //省去
printf("this is in shelln"); //调试信息
g_shellCB = shellCB;
return OsShellCreateTask(shellCB); //创建一个shell任务
}
在创建shell task
static int OsShellCreateTask(ShellCB *shellCB)
{
struct sched_param param = { 0 };
int ret;
ret = sched_getparam(getpid(), ¶m);
if (ret != SH_OK) {
goto OUT;
}
param.sched_priority = SHELL_PROCESS_PRIORITY_INIT;
ret = sched_setparam(getpid(), ¶m);
if (ret != SH_OK) {
goto OUT;
}
printf("ShellTaskInitn"); //打印调试信息
ret = ShellTaskInit(shellCB); //在这里会创建shelltask 线程
if (ret != SH_OK) {
goto OUT;
}
printf("ShellEntryInit"); //打印调试信息
ret = ShellEntryInit(shellCB); //创建shellentry线程
if (ret != SH_OK) {
goto OUT;
}
(void)pthread_join(shellCB->shellTaskHandle, NULL);
(void)pthread_join(shellCB->shellEntryHandle, NULL);
OUT:
ShellDeinit(shellCB);
return ret;
}
以上两个线程就是用来解析和执行我们在控制台输入的命令
对于shelltask
code-1.0kernelliteos_aappsshellsrcShmsg.c
void *ShellTask(void *argv)
{
int ret;
ShellCB *shellCB = (ShellCB *)argv;
if (shellCB == NULL) {
return NULL;
}
ret = prctl(PR_SET_NAME, "ShellTask");
if (ret != SH_OK) {
return NULL;
}
printf(SHELL_PROMPT);//修改这个宏用来测试 #define SHELL_PROMPT COLOR_L_RED"MY_OHOS # "COLOR_NONE
while (1) {
ret = ShellPend(shellCB);
if (ret == SH_OK) {
ShellCmdProcess(shellCB);
} else if (ret != SH_OK) {
break;
}
}
return NULL;
}
以上就是liteos启动的大致流程。最后放两张测试的图片,主要是看下我们在代码中添加的调试信息。