鸿蒙轻内核A核源码分析系列七 进程管理 (3) 原创 精华

zhushangyuan_
发布于 2022-3-30 22:55
浏览
2收藏

鸿蒙轻内核A核源码分析系列七 进程管理 (3)

【本文正在参与优质创作者激励】

本文会继续分析进程和任务管理模块。本文中所涉及的源码,以OpenHarmony LiteOS-A内核为例,均可以在开源站点https://gitee.com/openharmony/kernel_liteos_a 获取。如果涉及开发板,则默认以hispark_taurus为例。

本文记录下进程相关的初始化函数,如OsSystemProcessCreate、OsProcessInit、OsProcessCreateInit、OsUserInitProcess、OsDeInitPCB、OsUserInitProcessStart等。

1、LiteOS-A内核进程创建初始化通用函数

先看看一些内部函数,不管是初始化用户态进程还是内核态进程,都会使用这些函数,包含进程控制块初始化函数OsInitPCB、进程控制块初始化恢复函数OsDeInitPCB

1.1 进程控制块初始化函数OsInitPCB

进程控制块初始化函数OsInitPCB需要3个参数,第一个参数processCB是进程块指针,第二个参数为进程模式mode,分为内核态进程OS_KERNEL_MODE和用户态进程OS_USER_MODE。第三个参数用于设置进程名称。返回值为初始化成功LOS_OK还是失败LOS_ENOMEM。看下代码,⑴处设置进程控制块的信息,用户态进程还是内核态进程,进程状态设置为初始化状态,线程组编号设置为无效值,设置为默认掩码,定时器编号设置为无效值。⑵处初始化进程的双向链表。如果系统配置支持虚拟内存,则执行⑶判断初始化的进程是否为用户态进程,如果是用户态进程,则创建虚拟地址空间,如果创建失败,则把进程状态设置为未使用状态,然后返回错误码。⑷处表示如果是内核态进程,则指定进程的内核进程虚拟地址空间。有关虚拟地址空间的信息,请参考之前的系列文章。

如果执行CPUP特性,则执行⑸处代码,则为CPUP结构体申请内存空间。⑹处,如果开启了LOSCFG_SECURITY_VID,则V初始化ID映射链表。⑺处,如果开启了安全能力LOSCFG_SECURITY_CAPABILITY,则进行相应的初始化。⑻处为进程设置一个名称。

    STATIC UINT32 OsInitPCB(LosProcessCB *processCB, UINT32 mode, const CHAR *name)
    {
⑴      processCB->processMode = mode;
        processCB->processStatus = OS_PROCESS_STATUS_INIT;
        processCB->parentProcessID = OS_INVALID_VALUE;
        processCB->threadGroupID = OS_INVALID_VALUE;
        processCB->umask = OS_PROCESS_DEFAULT_UMASK;
        processCB->timerID = (timer_t)(UINTPTR)MAX_INVALID_TIMER_VID;

⑵      LOS_ListInit(&processCB->threadSiblingList);
        LOS_ListInit(&processCB->childrenList);
        LOS_ListInit(&processCB->exitChildList);
        LOS_ListInit(&(processCB->waitList));

    #ifdef LOSCFG_KERNEL_VM
⑶      if (OsProcessIsUserMode(processCB)) {
            processCB->vmSpace = OsCreateUserVmSpace();
            if (processCB->vmSpace == NULL) {
                processCB->processStatus = OS_PROCESS_FLAG_UNUSED;
                return LOS_ENOMEM;
            }
        } else {
⑷          processCB->vmSpace = LOS_GetKVmSpace();
        }
    #endif

    #ifdef LOSCFG_KERNEL_CPUP
⑸      processCB->processCpup = (OsCpupBase *)LOS_MemAlloc(m_aucSysMem1, sizeof(OsCpupBase));
        if (processCB->processCpup == NULL) {
            return LOS_ENOMEM;
        }
        (VOID)memset_s(processCB->processCpup, sizeof(OsCpupBase), 0, sizeof(OsCpupBase));
    #endif

    #ifdef LOSCFG_SECURITY_VID
⑹      status_t status = VidMapListInit(processCB);
        if (status != LOS_OK) {
            return LOS_ENOMEM;
        }
    #endif

⑺  #ifdef LOSCFG_SECURITY_CAPABILITY
        OsInitCapability(processCB);
    #endif

⑻      if (OsSetProcessName(processCB, name) != LOS_OK) {
            return LOS_ENOMEM;
        }

        return LOS_OK;
    }

1.2 进程控制块初始化恢复函数OsDeInitPCB

在创建进程时,会执行该函数,恢复进程控制块信息到初始化之前的状态。⑴处释放进程的资源,包含地址空间、文件、安全能力、定时器等占用的内存。如果存在父进程,则执行⑵从父进程的兄弟列表上删除。如果进程属于进程组,则从进程组中退出。然后执行⑷设置进程状态为退出态,把进程放入待回收链表中。

    STATIC VOID OsDeInitPCB(LosProcessCB *processCB)
    {
        UINT32 intSave;
        ProcessGroup *group = NULL;

        if (processCB == NULL) {
            return;
        }

⑴      OsProcessResourcesToFree(processCB);

        SCHEDULER_LOCK(intSave);
        if (processCB->parentProcessID != OS_INVALID_VALUE) {
⑵          LOS_ListDelete(&processCB->siblingList);
            processCB->parentProcessID = OS_INVALID_VALUE;
        }

⑶      if (processCB->group != NULL) {
            OsExitProcessGroup(processCB, &group);
        }

⑷      processCB->processStatus &= ~OS_PROCESS_STATUS_INIT;
        processCB->processStatus |= OS_PROCESS_FLAG_EXIT;
        LOS_ListHeadInsert(&g_processRecycleList, &processCB->pendList);
        SCHEDULER_UNLOCK(intSave);

        (VOID)LOS_MemFree(m_aucSysMem1, group);
        OsWriteResourceEvent(OS_RESOURCE_EVENT_FREE);
        return;
    }

2、 LiteOS-A内核系统进程创建函数OsSystemProcessCreate

系统进程创建函数OsSystemProcessCreate在文件kernel\common\los_config.c中被调用,在系统启动阶段创建系统进程。该函数又调用OsProcessInit,我们首先看下函数OsProcessInit。

2.1 进程初始化函数OsProcessInit

进程初始化函数OsProcessInit为进程控制块申请内存,初始化进程相关的进程链表。我们看下代码,⑴处获取配置的进程最大数值,然后计算需要的内存大小。⑵处申请内存,初始化申请的内存块。⑶处初始化空闲进程双向链表和待回收进程双向链表。
⑷处初始化每一个进程,社区进程编号、进程状态,然后把每一个进程放到空闲进程链表里。⑸处设置Idle进程编号为0,用户根进程编号为1,系统根进程编号为2,然后执行LOS_ListDelete把这3个进程从阻塞链表上删除。

    STATIC UINT32 OsProcessInit(VOID)
    {
        UINT32 index;
        UINT32 size;

⑴      g_processMaxNum = LOSCFG_BASE_CORE_PROCESS_LIMIT;
        size = g_processMaxNum * sizeof(LosProcessCB);

⑵      g_processCBArray = (LosProcessCB *)LOS_MemAlloc(m_aucSysMem1, size);
        if (g_processCBArray == NULL) {
            return LOS_NOK;
        }
        (VOID)memset_s(g_processCBArray, size, 0, size);

⑶      LOS_ListInit(&g_freeProcess);
        LOS_ListInit(&g_processRecycleList);

⑷      for (index = 0; index < g_processMaxNum; index++) {
            g_processCBArray[index].processID = index;
            g_processCBArray[index].processStatus = OS_PROCESS_FLAG_UNUSED;
            LOS_ListTailInsert(&g_freeProcess, &g_processCBArray[index].pendList);
        }

⑸      g_kernelIdleProcess = 0; /* 0: The idle process ID of the kernel-mode process is fixed at 0 */
        LOS_ListDelete(&OS_PCB_FROM_PID(g_kernelIdleProcess)->pendList);

        g_userInitProcess = 1; /* 1: The root process ID of the user-mode process is fixed at 1 */
        LOS_ListDelete(&OS_PCB_FROM_PID(g_userInitProcess)->pendList);

        g_kernelInitProcess = 2; /* 2: The root process ID of the kernel-mode process is fixed at 2 */
        LOS_ListDelete(&OS_PCB_FROM_PID(g_kernelInitProcess)->pendList);

        return LOS_OK;
    }

2.2 进程创建初始化函数OsProcessCreateInit

该函数用于创建进程时的一些初始化操作,主要是文件系统、安全能力、进程组等等。需要3个参数,分别是进程控制块指针,标记用户态还是内核态进程的flags,进程名称name。首先执行⑴调用OsInitPCB()对进程控制块进行初始化,然后,如果配置支持了虚拟文件系统VFS,则执行⑵为进程分别文件。⑶处根据进程号创建进程组。如果支持安全能力,则执行⑷创建用户。如果初始化失败,会执行⑸恢复进程控制块到初始化之前的状态。

    STATIC UINT32 OsProcessCreateInit(LosProcessCB *processCB, UINT32 flags, const CHAR *name)
    {
        ProcessGroup *group = NULL;
⑴      UINT32 ret = OsInitPCB(processCB, flags, name);
        if (ret != LOS_OK) {
            goto EXIT;
        }

    #ifdef LOSCFG_FS_VFS
⑵      processCB->files = alloc_files();
        if (processCB->files == NULL) {
            ret = LOS_ENOMEM;
            goto EXIT;
        }
    #endif

⑶      group = OsCreateProcessGroup(processCB->processID);
        if (group == NULL) {
            ret = LOS_ENOMEM;
            goto EXIT;
        }

    #ifdef LOSCFG_SECURITY_CAPABILITY
⑷      processCB->user = OsCreateUser(0, 0, 1);
        if (processCB->user == NULL) {
            ret = LOS_ENOMEM;
            goto EXIT;
        }
    #endif

        return LOS_OK;

    EXIT:
⑸      OsDeInitPCB(processCB);
        return ret;
    }

2.3 系统进程创建函数OsSystemProcessCreate

接下来,我们看看系统进程创建函数OsSystemProcessCreate的源代码。⑴处开始先后调用OsProcessInit、OsProcessCreateInit初始化内核态根进程,⑵处进程的状态不再是初始化状态。⑶处从内核进程获取全局进程组指针g_processGroup,然后初始化进程组的双向链表。这样看来,所有的进程组都会挂载到内核进程的进程组节点上。⑷处初始化内核空闲进程,内核空闲进程的父进程也是内核态根进程,插入到进程组链表,设置空闲进程的状态不再是初始化状态。执行⑸创建空闲任务,然后设置空闲进程的线程组编号为空闲线程编号。

    LITE_OS_SEC_TEXT_INIT UINT32 OsSystemProcessCreate(VOID)
    {
⑴      UINT32 ret = OsProcessInit();
        if (ret != LOS_OK) {
            return ret;
        }

        LosProcessCB *kerInitProcess = OS_PCB_FROM_PID(g_kernelInitProcess);
        ret = OsProcessCreateInit(kerInitProcess, OS_KERNEL_MODE, "KProcess");
        if (ret != LOS_OK) {
            return ret;
        }

⑵      kerInitProcess->processStatus &= ~OS_PROCESS_STATUS_INIT;
⑶      g_processGroup = kerInitProcess->group;
        LOS_ListInit(&g_processGroup->groupList);

⑷      LosProcessCB *idleProcess = OS_PCB_FROM_PID(g_kernelIdleProcess);
        ret = OsInitPCB(idleProcess, OS_KERNEL_MODE, "KIdle");
        if (ret != LOS_OK) {
            return ret;
        }
        idleProcess->parentProcessID = kerInitProcess->processID;
        LOS_ListTailInsert(&kerInitProcess->childrenList, &idleProcess->siblingList);
        idleProcess->group = kerInitProcess->group;
        LOS_ListTailInsert(&kerInitProcess->group->processList, &idleProcess->subordinateGroupList);
    #ifdef LOSCFG_SECURITY_CAPABILITY
        idleProcess->user = kerInitProcess->user;
    #endif
    #ifdef LOSCFG_FS_VFS
        idleProcess->files = kerInitProcess->files;
    #endif
        idleProcess->processStatus &= ~OS_PROCESS_STATUS_INIT;

⑸      ret = OsIdleTaskCreate();
        if (ret != LOS_OK) {
            return ret;
        }
        idleProcess->threadGroupID = OsGetIdleTaskId();

        return LOS_OK;
    }

3. LiteOS-A内核用户进程创建函数OsUserInitProcess

系统启动阶段,OsUserInitProcess启动init进程。该函数在device开发板目录下系统初始化文件中调用,如system_init.c等等。该函数需要开启LOSCFG_KERNEL_DYNLOAD宏,否则函数体内容为空。在阅读函数OsUserInitProcess的源码前,先看看该函数调用的其他函数,如OsLoadUserInit、OsUserInitStackAlloc、OsUserInitProcessStart等等。

3.1 加载用户初始化数据函数OsLoadUserInit

该函数用于加载用户数据。⑴处从链接脚本获取text、bss的开启和结束地址,然后计算bss段、初始化段的大小。⑵处进行一些必要的校验,是否针对内存页对齐,内存段长度是否合理等。然后执行⑶,申请物理内存页,然后把用户初始化数据复制到申请的内存页。⑷处进行虚实映射。如果bss段长度不为0,执行⑸把bss段的内存区域清零。

STATIC UINT32 OsLoadUserInit(LosProcessCB *processCB)
{
    /*              userInitTextStart               -----
     * | user text |
     *
     * | user data |                                initSize
     *              userInitBssStart  ---
     * | user bss  |                  initBssSize
     *              userInitEnd       ---           -----
     */
    errno_t errRet;
    INT32 ret;
⑴  CHAR *userInitTextStart = (CHAR *)&__user_init_entry;
    CHAR *userInitBssStart = (CHAR *)&__user_init_bss;
    CHAR *userInitEnd = (CHAR *)&__user_init_end;
    UINT32 initBssSize = userInitEnd - userInitBssStart;
    UINT32 initSize = userInitEnd - userInitTextStart;
    VOID *userBss = NULL;
    VOID *userText = NULL;

⑵  if ((LOS_Align((UINTPTR)userInitTextStart, PAGE_SIZE) != (UINTPTR)userInitTextStart) ||
        (LOS_Align((UINTPTR)userInitEnd, PAGE_SIZE) != (UINTPTR)userInitEnd)) {
        return LOS_EINVAL;
    }

    if ((initSize == 0) || (initSize <= initBssSize)) {
        return LOS_EINVAL;
    }

⑶  userText = LOS_PhysPagesAllocContiguous(initSize >> PAGE_SHIFT);
    if (userText == NULL) {
        return LOS_NOK;
    }

    errRet = memcpy_s(userText, initSize, (VOID *)&__user_init_load_addr, initSize - initBssSize);
    if (errRet != EOK) {
        PRINT_ERR("Load user init text, data and bss failed! err : %d\n", errRet);
        goto ERROR;
    }
⑷  ret = LOS_VaddrToPaddrMmap(processCB->vmSpace, (VADDR_T)(UINTPTR)userInitTextStart, LOS_PaddrQuery(userText),
                               initSize, VM_MAP_REGION_FLAG_PERM_READ | VM_MAP_REGION_FLAG_PERM_WRITE |
                               VM_MAP_REGION_FLAG_FIXED | VM_MAP_REGION_FLAG_PERM_EXECUTE |
                               VM_MAP_REGION_FLAG_PERM_USER);
    if (ret < 0) {
        PRINT_ERR("Mmap user init text, data and bss failed! err : %d\n", ret);
        goto ERROR;
    }

    /* The User init boot segment may not actually exist */
⑸  if (initBssSize != 0) {
        userBss = (VOID *)((UINTPTR)userText + userInitBssStart - userInitTextStart);
        errRet = memset_s(userBss, initBssSize, 0, initBssSize);
        if (errRet != EOK) {
            PRINT_ERR("memset user init bss failed! err : %d\n", errRet);
            goto ERROR;
        }
    }

    return LOS_OK;

ERROR:
    (VOID)LOS_PhysPagesFreeContiguous(userText, initSize >> PAGE_SHIFT);
    return LOS_NOK;
}

3.2 用户初始化栈函数OsUserInitStackAlloc

该函数需要2个参数,第一个参数为进程控制块,第二个参数为输出参数,用于获取用户任务栈的大小。⑴处用户任务栈大小对页进行对齐,然后申请内存区域,然后执行⑵设置内存区域类型,并社区内存区域标签为栈。然后设置输出参数为任务栈大小,并返回用户栈空间的开始地址。

STATIC VOID *OsUserInitStackAlloc(LosProcessCB *processCB, UINT32 *size)
{
    LosVmMapRegion *region = NULL;
⑴  UINT32 stackSize = ALIGN(OS_USER_TASK_STACK_SIZE, PAGE_SIZE);

    region = LOS_RegionAlloc(processCB->vmSpace, 0, stackSize,
                             VM_MAP_REGION_FLAG_PERM_USER | VM_MAP_REGION_FLAG_PERM_READ |
                             VM_MAP_REGION_FLAG_PERM_WRITE, 0);
    if (region == NULL) {
        return NULL;
    }

⑵  LOS_SetRegionTypeAnon(region);
    region->regionFlags |= VM_MAP_REGION_FLAG_STACK;

    *size = stackSize;

    return (VOID *)(UINTPTR)region->range.base;
}

3.3 用户进程初始化开启函数OsUserInitProcessStart

用户进程初始化开启函数OsUserInitProcessStart用于创建线程,设置调度策略等。⑴处为用户态进程创建个线程,然后为进程设置优先级。⑵处设置进程状态为非初始化状态,然后执行⑶为任务设置调度策略和优先级。

STATIC UINT32 OsUserInitProcessStart(LosProcessCB *processCB, TSK_INIT_PARAM_S *param)
{
    UINT32 intSave;
    INT32 ret;

⑴  UINT32 taskID = OsCreateUserTask(processCB->processID, param);
    if (taskID == OS_INVALID_VALUE) {
        return LOS_NOK;
    }

    ret = LOS_SetProcessPriority(processCB->processID, OS_PROCESS_USERINIT_PRIORITY);
    if (ret != LOS_OK) {
        PRINT_ERR("User init process set priority failed! ERROR:%d \n", ret);
        goto EXIT;
    }

    SCHEDULER_LOCK(intSave);
⑵  processCB->processStatus &= ~OS_PROCESS_STATUS_INIT;
    SCHEDULER_UNLOCK(intSave);

⑶  ret = LOS_SetTaskScheduler(taskID, LOS_SCHED_RR, OS_TASK_PRIORITY_LOWEST);
    if (ret != LOS_OK) {
        PRINT_ERR("User init process set scheduler failed! ERROR:%d \n", ret);
        goto EXIT;
    }

    return LOS_OK;

EXIT:
    (VOID)LOS_TaskDelete(taskID);
    return ret;
}

3.4 用户态进程初始化函数OsUserInitProcess

用户态进程初始化函数OsUserInitProcess完成用户态进程的初始化。⑴处获取用户态根进程,然后调用函数创建用户态根进程,并调用函数OsLoadUserInit加载用户初始化数据。⑵处初始化用户栈,然后设置线程的初始化参数,⑶处完成用户态进程的创建。

LITE_OS_SEC_TEXT_INIT UINT32 OsUserInitProcess(VOID)
{
    UINT32 ret;
    UINT32 size;
    TSK_INIT_PARAM_S param = { 0 };
    VOID *stack = NULL;

⑴  LosProcessCB *processCB = OS_PCB_FROM_PID(g_userInitProcess);
    ret = OsProcessCreateInit(processCB, OS_USER_MODE, "Init");
    if (ret != LOS_OK) {
        return ret;
    }

    ret = OsLoadUserInit(processCB);
    if (ret != LOS_OK) {
        goto ERROR;
    }

⑵  stack = OsUserInitStackAlloc(processCB, &size);
    if (stack == NULL) {
        PRINT_ERR("Alloc user init process user stack failed!\n");
        goto ERROR;
    }

    param.pfnTaskEntry = (TSK_ENTRY_FUNC)(CHAR *)&__user_init_entry;
    param.userParam.userSP = (UINTPTR)stack + size;
    param.userParam.userMapBase = (UINTPTR)stack;
    param.userParam.userMapSize = size;
    param.uwResved = OS_TASK_FLAG_PTHREAD_JOIN;
⑶  ret = OsUserInitProcessStart(processCB, &param);
    if (ret != LOS_OK) {
        (VOID)OsUnMMap(processCB->vmSpace, param.userParam.userMapBase, param.userParam.userMapSize);
        goto ERROR;
    }

    return LOS_OK;

ERROR:
    OsDeInitPCB(processCB);
    return ret;
}

参考站点

小结

本文介绍了进程管理的内核进程、用户态进程的初始化相关函数。因为时间关系,仓促写作,或能力限制,若有失误之处,请各位读者多多指正。感谢阅读,有什么问题,请留言。

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
已于2022-3-30 22:55:38修改
2
收藏 2
回复
举报
1条回复
按时间正序
/
按时间倒序
ELT_ZIP
ELT_ZIP

祝老师太硬核la

1
回复
2022-3-30 23:09:55
回复
    相关推荐